SOPObject.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Security;
  30. using OpenMetaverse;
  31. using OpenMetaverse.Packets;
  32. using OpenSim.Framework;
  33. using OpenSim.Region.Framework.Scenes;
  34. using OpenSim.Region.OptionalModules.Scripting.Minimodule.Object;
  35. using OpenSim.Region.Physics.Manager;
  36. using PrimType=OpenSim.Region.OptionalModules.Scripting.Minimodule.Object.PrimType;
  37. using SculptType=OpenSim.Region.OptionalModules.Scripting.Minimodule.Object.SculptType;
  38. namespace OpenSim.Region.OptionalModules.Scripting.Minimodule
  39. {
  40. class SOPObject : MarshalByRefObject, IObject, IObjectPhysics, IObjectShape, IObjectSound
  41. {
  42. private readonly Scene m_rootScene;
  43. private readonly uint m_localID;
  44. private readonly ISecurityCredential m_security;
  45. [Obsolete("Replace with 'credential' constructor [security]")]
  46. public SOPObject(Scene rootScene, uint localID)
  47. {
  48. m_rootScene = rootScene;
  49. m_localID = localID;
  50. }
  51. public SOPObject(Scene rootScene, uint localID, ISecurityCredential credential)
  52. {
  53. m_rootScene = rootScene;
  54. m_localID = localID;
  55. m_security = credential;
  56. }
  57. /// <summary>
  58. /// This needs to run very, very quickly.
  59. /// It is utilized in nearly every property and method.
  60. /// </summary>
  61. /// <returns></returns>
  62. private SceneObjectPart GetSOP()
  63. {
  64. return m_rootScene.GetSceneObjectPart(m_localID);
  65. }
  66. private bool CanEdit()
  67. {
  68. if (!m_security.CanEditObject(this))
  69. {
  70. throw new SecurityException("Insufficient Permission to edit object with UUID [" + GetSOP().UUID + "]");
  71. }
  72. return true;
  73. }
  74. #region OnTouch
  75. private event OnTouchDelegate _OnTouch;
  76. private bool _OnTouchActive = false;
  77. public event OnTouchDelegate OnTouch
  78. {
  79. add
  80. {
  81. if (CanEdit())
  82. {
  83. if (!_OnTouchActive)
  84. {
  85. GetSOP().Flags |= PrimFlags.Touch;
  86. _OnTouchActive = true;
  87. m_rootScene.EventManager.OnObjectGrab += EventManager_OnObjectGrab;
  88. }
  89. _OnTouch += value;
  90. }
  91. }
  92. remove
  93. {
  94. _OnTouch -= value;
  95. if (_OnTouch == null)
  96. {
  97. GetSOP().Flags &= ~PrimFlags.Touch;
  98. _OnTouchActive = false;
  99. m_rootScene.EventManager.OnObjectGrab -= EventManager_OnObjectGrab;
  100. }
  101. }
  102. }
  103. void EventManager_OnObjectGrab(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
  104. {
  105. if (_OnTouchActive && m_localID == localID)
  106. {
  107. TouchEventArgs e = new TouchEventArgs();
  108. e.Avatar = new SPAvatar(m_rootScene, remoteClient.AgentId, m_security);
  109. e.TouchBiNormal = surfaceArgs.Binormal;
  110. e.TouchMaterialIndex = surfaceArgs.FaceIndex;
  111. e.TouchNormal = surfaceArgs.Normal;
  112. e.TouchPosition = surfaceArgs.Position;
  113. e.TouchST = new Vector2(surfaceArgs.STCoord.X, surfaceArgs.STCoord.Y);
  114. e.TouchUV = new Vector2(surfaceArgs.UVCoord.X, surfaceArgs.UVCoord.Y);
  115. IObject sender = this;
  116. if (_OnTouch != null)
  117. _OnTouch(sender, e);
  118. }
  119. }
  120. #endregion
  121. public bool Exists
  122. {
  123. get { return GetSOP() != null; }
  124. }
  125. public uint LocalID
  126. {
  127. get { return m_localID; }
  128. }
  129. public UUID GlobalID
  130. {
  131. get { return GetSOP().UUID; }
  132. }
  133. public string Name
  134. {
  135. get { return GetSOP().Name; }
  136. set
  137. {
  138. if (CanEdit())
  139. GetSOP().Name = value;
  140. }
  141. }
  142. public string Description
  143. {
  144. get { return GetSOP().Description; }
  145. set
  146. {
  147. if (CanEdit())
  148. GetSOP().Description = value;
  149. }
  150. }
  151. public IObject[] Children
  152. {
  153. get
  154. {
  155. SceneObjectPart my = GetSOP();
  156. int total = my.ParentGroup.Children.Count;
  157. IObject[] rets = new IObject[total];
  158. int i = 0;
  159. foreach (KeyValuePair<UUID, SceneObjectPart> pair in my.ParentGroup.Children)
  160. {
  161. rets[i++] = new SOPObject(m_rootScene, pair.Value.LocalId, m_security);
  162. }
  163. return rets;
  164. }
  165. }
  166. public IObject Root
  167. {
  168. get { return new SOPObject(m_rootScene, GetSOP().ParentGroup.RootPart.LocalId, m_security); }
  169. }
  170. public IObjectMaterial[] Materials
  171. {
  172. get
  173. {
  174. SceneObjectPart sop = GetSOP();
  175. IObjectMaterial[] rets = new IObjectMaterial[getNumberOfSides(sop)];
  176. for (int i = 0; i < rets.Length; i++)
  177. {
  178. rets[i] = new SOPObjectMaterial(i, sop);
  179. }
  180. return rets;
  181. }
  182. }
  183. public Vector3 Scale
  184. {
  185. get { return GetSOP().Scale; }
  186. set
  187. {
  188. if (CanEdit())
  189. GetSOP().Scale = value;
  190. }
  191. }
  192. public Quaternion WorldRotation
  193. {
  194. get { throw new System.NotImplementedException(); }
  195. set { throw new System.NotImplementedException(); }
  196. }
  197. public Quaternion OffsetRotation
  198. {
  199. get { throw new System.NotImplementedException(); }
  200. set { throw new System.NotImplementedException(); }
  201. }
  202. public Vector3 WorldPosition
  203. {
  204. get { return GetSOP().AbsolutePosition; }
  205. set
  206. {
  207. if (CanEdit())
  208. {
  209. SceneObjectPart pos = GetSOP();
  210. pos.UpdateOffSet(value - pos.AbsolutePosition);
  211. }
  212. }
  213. }
  214. public Vector3 OffsetPosition
  215. {
  216. get { return GetSOP().OffsetPosition; }
  217. set
  218. {
  219. if (CanEdit())
  220. {
  221. GetSOP().OffsetPosition = value;
  222. }
  223. }
  224. }
  225. public Vector3 SitTarget
  226. {
  227. get { return GetSOP().SitTargetPosition; }
  228. set
  229. {
  230. if (CanEdit())
  231. {
  232. GetSOP().SitTargetPosition = value;
  233. }
  234. }
  235. }
  236. public string SitTargetText
  237. {
  238. get { return GetSOP().SitName; }
  239. set
  240. {
  241. if (CanEdit())
  242. {
  243. GetSOP().SitName = value;
  244. }
  245. }
  246. }
  247. public string TouchText
  248. {
  249. get { return GetSOP().TouchName; }
  250. set
  251. {
  252. if (CanEdit())
  253. {
  254. GetSOP().TouchName = value;
  255. }
  256. }
  257. }
  258. public string Text
  259. {
  260. get { return GetSOP().Text; }
  261. set
  262. {
  263. if (CanEdit())
  264. {
  265. GetSOP().SetText(value,new Vector3(1.0f,1.0f,1.0f),1.0f);
  266. }
  267. }
  268. }
  269. public bool IsRotationLockedX
  270. {
  271. get { throw new System.NotImplementedException(); }
  272. set { throw new System.NotImplementedException(); }
  273. }
  274. public bool IsRotationLockedY
  275. {
  276. get { throw new System.NotImplementedException(); }
  277. set { throw new System.NotImplementedException(); }
  278. }
  279. public bool IsRotationLockedZ
  280. {
  281. get { throw new System.NotImplementedException(); }
  282. set { throw new System.NotImplementedException(); }
  283. }
  284. public bool IsSandboxed
  285. {
  286. get { throw new System.NotImplementedException(); }
  287. set { throw new System.NotImplementedException(); }
  288. }
  289. public bool IsImmotile
  290. {
  291. get { throw new System.NotImplementedException(); }
  292. set { throw new System.NotImplementedException(); }
  293. }
  294. public bool IsAlwaysReturned
  295. {
  296. get { throw new System.NotImplementedException(); }
  297. set { throw new System.NotImplementedException(); }
  298. }
  299. public bool IsTemporary
  300. {
  301. get { throw new System.NotImplementedException(); }
  302. set { throw new System.NotImplementedException(); }
  303. }
  304. public bool IsFlexible
  305. {
  306. get { throw new System.NotImplementedException(); }
  307. set { throw new System.NotImplementedException(); }
  308. }
  309. public PhysicsMaterial PhysicsMaterial
  310. {
  311. get { throw new System.NotImplementedException(); }
  312. set { throw new System.NotImplementedException(); }
  313. }
  314. public IObjectPhysics Physics
  315. {
  316. get { return this; }
  317. }
  318. public IObjectShape Shape
  319. {
  320. get { return this; }
  321. }
  322. public IObjectInventory Inventory
  323. {
  324. get { return new SOPObjectInventory(m_rootScene, GetSOP().TaskInventory); }
  325. }
  326. #region Public Functions
  327. public void Say(string msg)
  328. {
  329. if (!CanEdit())
  330. return;
  331. SceneObjectPart sop = GetSOP();
  332. m_rootScene.SimChat(msg, ChatTypeEnum.Say, sop.AbsolutePosition, sop.Name, sop.UUID, false);
  333. }
  334. public void Say(string msg,int channel)
  335. {
  336. if (!CanEdit())
  337. return;
  338. SceneObjectPart sop = GetSOP();
  339. m_rootScene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,channel, sop.AbsolutePosition, sop.Name, sop.UUID, false);
  340. }
  341. #endregion
  342. #region Supporting Functions
  343. // Helper functions to understand if object has cut, hollow, dimple, and other affecting number of faces
  344. private static void hasCutHollowDimpleProfileCut(int primType, PrimitiveBaseShape shape, out bool hasCut, out bool hasHollow,
  345. out bool hasDimple, out bool hasProfileCut)
  346. {
  347. if (primType == (int)PrimType.Box
  348. ||
  349. primType == (int)PrimType.Cylinder
  350. ||
  351. primType == (int)PrimType.Prism)
  352. hasCut = (shape.ProfileBegin > 0) || (shape.ProfileEnd > 0);
  353. else
  354. hasCut = (shape.PathBegin > 0) || (shape.PathEnd > 0);
  355. hasHollow = shape.ProfileHollow > 0;
  356. hasDimple = (shape.ProfileBegin > 0) || (shape.ProfileEnd > 0); // taken from llSetPrimitiveParms
  357. hasProfileCut = hasDimple; // is it the same thing?
  358. }
  359. private static int getScriptPrimType(PrimitiveBaseShape primShape)
  360. {
  361. if (primShape.SculptEntry)
  362. return (int) PrimType.Sculpt;
  363. if ((primShape.ProfileCurve & 0x07) == (byte) ProfileShape.Square)
  364. {
  365. if (primShape.PathCurve == (byte) Extrusion.Straight)
  366. return (int) PrimType.Box;
  367. if (primShape.PathCurve == (byte) Extrusion.Curve1)
  368. return (int) PrimType.Tube;
  369. }
  370. else if ((primShape.ProfileCurve & 0x07) == (byte) ProfileShape.Circle)
  371. {
  372. if (primShape.PathCurve == (byte) Extrusion.Straight)
  373. return (int) PrimType.Cylinder;
  374. if (primShape.PathCurve == (byte) Extrusion.Curve1)
  375. return (int) PrimType.Torus;
  376. }
  377. else if ((primShape.ProfileCurve & 0x07) == (byte) ProfileShape.HalfCircle)
  378. {
  379. if (primShape.PathCurve == (byte) Extrusion.Curve1 || primShape.PathCurve == (byte) Extrusion.Curve2)
  380. return (int) PrimType.Sphere;
  381. }
  382. else if ((primShape.ProfileCurve & 0x07) == (byte) ProfileShape.EquilateralTriangle)
  383. {
  384. if (primShape.PathCurve == (byte) Extrusion.Straight)
  385. return (int) PrimType.Prism;
  386. if (primShape.PathCurve == (byte) Extrusion.Curve1)
  387. return (int) PrimType.Ring;
  388. }
  389. return (int) PrimType.NotPrimitive;
  390. }
  391. private static int getNumberOfSides(SceneObjectPart part)
  392. {
  393. int ret;
  394. bool hasCut;
  395. bool hasHollow;
  396. bool hasDimple;
  397. bool hasProfileCut;
  398. int primType = getScriptPrimType(part.Shape);
  399. hasCutHollowDimpleProfileCut(primType, part.Shape, out hasCut, out hasHollow, out hasDimple, out hasProfileCut);
  400. switch (primType)
  401. {
  402. default:
  403. case (int) PrimType.Box:
  404. ret = 6;
  405. if (hasCut) ret += 2;
  406. if (hasHollow) ret += 1;
  407. break;
  408. case (int) PrimType.Cylinder:
  409. ret = 3;
  410. if (hasCut) ret += 2;
  411. if (hasHollow) ret += 1;
  412. break;
  413. case (int) PrimType.Prism:
  414. ret = 5;
  415. if (hasCut) ret += 2;
  416. if (hasHollow) ret += 1;
  417. break;
  418. case (int) PrimType.Sphere:
  419. ret = 1;
  420. if (hasCut) ret += 2;
  421. if (hasDimple) ret += 2;
  422. if (hasHollow)
  423. ret += 1; // GOTCHA: LSL shows 2 additional sides here.
  424. // This has been fixed, but may cause porting issues.
  425. break;
  426. case (int) PrimType.Torus:
  427. ret = 1;
  428. if (hasCut) ret += 2;
  429. if (hasProfileCut) ret += 2;
  430. if (hasHollow) ret += 1;
  431. break;
  432. case (int) PrimType.Tube:
  433. ret = 4;
  434. if (hasCut) ret += 2;
  435. if (hasProfileCut) ret += 2;
  436. if (hasHollow) ret += 1;
  437. break;
  438. case (int) PrimType.Ring:
  439. ret = 3;
  440. if (hasCut) ret += 2;
  441. if (hasProfileCut) ret += 2;
  442. if (hasHollow) ret += 1;
  443. break;
  444. case (int) PrimType.Sculpt:
  445. ret = 1;
  446. break;
  447. }
  448. return ret;
  449. }
  450. #endregion
  451. #region IObjectPhysics
  452. public bool Enabled
  453. {
  454. get { throw new System.NotImplementedException(); }
  455. set { throw new System.NotImplementedException(); }
  456. }
  457. public bool Phantom
  458. {
  459. get { throw new System.NotImplementedException(); }
  460. set { throw new System.NotImplementedException(); }
  461. }
  462. public bool PhantomCollisions
  463. {
  464. get { throw new System.NotImplementedException(); }
  465. set { throw new System.NotImplementedException(); }
  466. }
  467. public double Density
  468. {
  469. get { return (GetSOP().PhysActor.Mass/Scale.X*Scale.Y/Scale.Z); }
  470. set { throw new NotImplementedException(); }
  471. }
  472. public double Mass
  473. {
  474. get { return GetSOP().PhysActor.Mass; }
  475. set { throw new NotImplementedException(); }
  476. }
  477. public double Buoyancy
  478. {
  479. get { return GetSOP().PhysActor.Buoyancy; }
  480. set { GetSOP().PhysActor.Buoyancy = (float)value; }
  481. }
  482. public Vector3 GeometricCenter
  483. {
  484. get
  485. {
  486. Vector3 tmp = GetSOP().PhysActor.GeometricCenter;
  487. return tmp;
  488. }
  489. }
  490. public Vector3 CenterOfMass
  491. {
  492. get
  493. {
  494. Vector3 tmp = GetSOP().PhysActor.CenterOfMass;
  495. return tmp;
  496. }
  497. }
  498. public Vector3 RotationalVelocity
  499. {
  500. get
  501. {
  502. Vector3 tmp = GetSOP().PhysActor.RotationalVelocity;
  503. return tmp;
  504. }
  505. set
  506. {
  507. if (!CanEdit())
  508. return;
  509. GetSOP().PhysActor.RotationalVelocity = value;
  510. }
  511. }
  512. public Vector3 Velocity
  513. {
  514. get
  515. {
  516. Vector3 tmp = GetSOP().PhysActor.Velocity;
  517. return tmp;
  518. }
  519. set
  520. {
  521. if (!CanEdit())
  522. return;
  523. GetSOP().PhysActor.Velocity = value;
  524. }
  525. }
  526. public Vector3 Torque
  527. {
  528. get
  529. {
  530. Vector3 tmp = GetSOP().PhysActor.Torque;
  531. return tmp;
  532. }
  533. set
  534. {
  535. if (!CanEdit())
  536. return;
  537. GetSOP().PhysActor.Torque = value;
  538. }
  539. }
  540. public Vector3 Acceleration
  541. {
  542. get
  543. {
  544. Vector3 tmp = GetSOP().PhysActor.Acceleration;
  545. return tmp;
  546. }
  547. }
  548. public Vector3 Force
  549. {
  550. get
  551. {
  552. Vector3 tmp = GetSOP().PhysActor.Force;
  553. return tmp;
  554. }
  555. set
  556. {
  557. if (!CanEdit())
  558. return;
  559. GetSOP().PhysActor.Force = value;
  560. }
  561. }
  562. public bool FloatOnWater
  563. {
  564. set
  565. {
  566. if (!CanEdit())
  567. return;
  568. GetSOP().PhysActor.FloatOnWater = value;
  569. }
  570. }
  571. public void AddForce(Vector3 force, bool pushforce)
  572. {
  573. if (!CanEdit())
  574. return;
  575. GetSOP().PhysActor.AddForce(force, pushforce);
  576. }
  577. public void AddAngularForce(Vector3 force, bool pushforce)
  578. {
  579. if (!CanEdit())
  580. return;
  581. GetSOP().PhysActor.AddAngularForce(force, pushforce);
  582. }
  583. public void SetMomentum(Vector3 momentum)
  584. {
  585. if (!CanEdit())
  586. return;
  587. GetSOP().PhysActor.SetMomentum(momentum);
  588. }
  589. #endregion
  590. #region Implementation of IObjectShape
  591. private UUID m_sculptMap = UUID.Zero;
  592. public UUID SculptMap
  593. {
  594. get { return m_sculptMap; }
  595. set
  596. {
  597. if (!CanEdit())
  598. return;
  599. m_sculptMap = value;
  600. SetPrimitiveSculpted(SculptMap, (byte) SculptType);
  601. }
  602. }
  603. private SculptType m_sculptType = Object.SculptType.Default;
  604. public SculptType SculptType
  605. {
  606. get { return m_sculptType; }
  607. set
  608. {
  609. if (!CanEdit())
  610. return;
  611. m_sculptType = value;
  612. SetPrimitiveSculpted(SculptMap, (byte) SculptType);
  613. }
  614. }
  615. public HoleShape HoleType
  616. {
  617. get { throw new System.NotImplementedException(); }
  618. set { throw new System.NotImplementedException(); }
  619. }
  620. public double HoleSize
  621. {
  622. get { throw new System.NotImplementedException(); }
  623. set { throw new System.NotImplementedException(); }
  624. }
  625. public PrimType PrimType
  626. {
  627. get { return (PrimType)getScriptPrimType(GetSOP().Shape); }
  628. set { throw new System.NotImplementedException(); }
  629. }
  630. private void SetPrimitiveSculpted(UUID map, byte type)
  631. {
  632. ObjectShapePacket.ObjectDataBlock shapeBlock = new ObjectShapePacket.ObjectDataBlock();
  633. SceneObjectPart part = GetSOP();
  634. UUID sculptId = map;
  635. shapeBlock.ObjectLocalID = part.LocalId;
  636. shapeBlock.PathScaleX = 100;
  637. shapeBlock.PathScaleY = 150;
  638. // retain pathcurve
  639. shapeBlock.PathCurve = part.Shape.PathCurve;
  640. part.Shape.SetSculptData((byte)type, sculptId);
  641. part.Shape.SculptEntry = true;
  642. part.UpdateShape(shapeBlock);
  643. }
  644. #endregion
  645. #region Implementation of IObjectSound
  646. public IObjectSound Sound
  647. {
  648. get { return this; }
  649. }
  650. public void Play(UUID asset, double volume)
  651. {
  652. if (!CanEdit())
  653. return;
  654. GetSOP().SendSound(asset.ToString(), volume, true, 0);
  655. }
  656. #endregion
  657. }
  658. }