aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs703
1 files changed, 703 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
new file mode 100644
index 0000000..546bcd9
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
@@ -0,0 +1,703 @@
1// Proprietary code of Avination Virtual Limited
2// (c) 2012 Melanie Thielker, Leal Duarte
3//
4
5using System;
6using System.IO;
7using System.Collections;
8using System.Collections.Generic;
9using System.Text;
10
11using OpenMetaverse;
12using OpenMetaverse.StructuredData;
13
14using OpenSim.Framework;
15using OpenSim.Region.Framework;
16using OpenSim.Region.Framework.Scenes;
17using OpenSim.Framework.Capabilities;
18
19using ComponentAce.Compression.Libs.zlib;
20
21using OSDArray = OpenMetaverse.StructuredData.OSDArray;
22using OSDMap = OpenMetaverse.StructuredData.OSDMap;
23
24namespace OpenSim.Region.ClientStack.Linden
25{
26 public struct ModelPrimLimits
27 {
28
29 }
30
31 public class ModelCost
32 {
33
34 // upload fee defaults
35 // fees are normalized to 1.0
36 // this parameters scale them to basic cost ( so 1.0 translates to 10 )
37
38 public float ModelMeshCostFactor = 0.0f; // scale total cost relative to basic (excluding textures)
39 public float ModelTextureCostFactor = 1.0f; // scale textures fee to basic.
40 public float ModelMinCostFactor = 0.0f; // 0.5f; // minimum total model free excluding textures
41
42 // itens costs in normalized values
43 // ie will be multiplied by basicCost and factors above
44 public float primCreationCost = 0.002f; // extra cost for each prim creation overhead
45 // weigthed size to normalized cost
46 public float bytecost = 1e-5f;
47
48 // mesh upload fees based on compressed data sizes
49 // several data sections are counted more that once
50 // to promote user optimization
51 // following parameters control how many extra times they are added
52 // to global size.
53 // LOD meshs
54 const float medSizeWth = 1f; // 2x
55 const float lowSizeWth = 1.5f; // 2.5x
56 const float lowestSizeWth = 2f; // 3x
57 // favor potencially physical optimized meshs versus automatic decomposition
58 const float physMeshSizeWth = 6f; // counts 7x
59 const float physHullSizeWth = 8f; // counts 9x
60
61 // stream cost area factors
62 // more or less like SL
63 const float highLodFactor = 17.36f;
64 const float midLodFactor = 277.78f;
65 const float lowLodFactor = 1111.11f;
66
67 // physics cost is below, identical to SL, assuming shape type convex
68 // server cost is below identical to SL assuming non scripted non physical object
69
70 // internal
71 const int bytesPerCoord = 6; // 3 coords, 2 bytes per each
72
73 // control prims dimensions
74 public float PrimScaleMin = 0.001f;
75 public float NonPhysicalPrimScaleMax = 256f;
76 public float PhysicalPrimScaleMax = 10f;
77 public int ObjectLinkedPartsMax = 512;
78
79 // storage for a single mesh asset cost parameters
80 private class ameshCostParam
81 {
82 // LOD sizes for size dependent streaming cost
83 public int highLODSize;
84 public int medLODSize;
85 public int lowLODSize;
86 public int lowestLODSize;
87 // normalized fee based on compressed data sizes
88 public float costFee;
89 // physics cost
90 public float physicsCost;
91 }
92
93 // calculates a mesh model costs
94 // returns false on error, with a reason on parameter error
95 // resources input LLSD request
96 // basicCost input region assets upload cost
97 // totalcost returns model total upload fee
98 // meshcostdata returns detailed costs for viewer
99 // avatarSkeleton if mesh includes a avatar skeleton
100 // useAvatarCollider if we should use physics mesh for avatar
101 public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost,
102 LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning)
103 {
104 totalcost = 0;
105 error = string.Empty;
106
107 bool avatarSkeleton = false;
108
109 if (resources == null ||
110 resources.instance_list == null ||
111 resources.instance_list.Array.Count == 0)
112 {
113 error = "missing model information.";
114 return false;
115 }
116
117 int numberInstances = resources.instance_list.Array.Count;
118
119 if( numberInstances > ObjectLinkedPartsMax )
120 {
121 error = "Model whould have more than " + ObjectLinkedPartsMax.ToString() + " linked prims";
122 return false;
123 }
124
125 meshcostdata.model_streaming_cost = 0.0;
126 meshcostdata.simulation_cost = 0.0;
127 meshcostdata.physics_cost = 0.0;
128 meshcostdata.resource_cost = 0.0;
129
130 meshcostdata.upload_price_breakdown.mesh_instance = 0;
131 meshcostdata.upload_price_breakdown.mesh_physics = 0;
132 meshcostdata.upload_price_breakdown.mesh_streaming = 0;
133 meshcostdata.upload_price_breakdown.model = 0;
134
135 int itmp;
136
137 // textures cost
138 if (resources.texture_list != null && resources.texture_list.Array.Count > 0)
139 {
140 float textures_cost = (float)(resources.texture_list.Array.Count * basicCost);
141 textures_cost *= ModelTextureCostFactor;
142
143 itmp = (int)(textures_cost + 0.5f); // round
144 meshcostdata.upload_price_breakdown.texture = itmp;
145 totalcost += itmp;
146 }
147
148 // meshs assets cost
149 float meshsfee = 0;
150 int numberMeshs = 0;
151 bool haveMeshs = false;
152
153 bool curskeleton;
154 bool curAvatarPhys;
155
156 List<ameshCostParam> meshsCosts = new List<ameshCostParam>();
157
158 if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0)
159 {
160 numberMeshs = resources.mesh_list.Array.Count;
161
162 for (int i = 0; i < numberMeshs; i++)
163 {
164 ameshCostParam curCost = new ameshCostParam();
165 byte[] data = (byte[])resources.mesh_list.Array[i];
166
167 if (!MeshCost(data, curCost,out curskeleton, out curAvatarPhys, out error))
168 {
169 return false;
170 }
171
172 if (curskeleton)
173 {
174 if (avatarSkeleton)
175 {
176 error = "model can only contain a avatar skeleton";
177 return false;
178 }
179 avatarSkeleton = true;
180 }
181 meshsCosts.Add(curCost);
182 meshsfee += curCost.costFee;
183 }
184 haveMeshs = true;
185 }
186
187 // instances (prims) cost
188
189
190 int mesh;
191 int skipedSmall = 0;
192 for (int i = 0; i < numberInstances; i++)
193 {
194 Hashtable inst = (Hashtable)resources.instance_list.Array[i];
195
196 ArrayList ascale = (ArrayList)inst["scale"];
197 Vector3 scale;
198 double tmp;
199 tmp = (double)ascale[0];
200 scale.X = (float)tmp;
201 tmp = (double)ascale[1];
202 scale.Y = (float)tmp;
203 tmp = (double)ascale[2];
204 scale.Z = (float)tmp;
205
206 if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin)
207 {
208 skipedSmall++;
209 continue;
210 }
211
212 if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax)
213 {
214 error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale";
215 return false;
216 }
217
218 if (haveMeshs && inst.ContainsKey("mesh"))
219 {
220 mesh = (int)inst["mesh"];
221
222 if (mesh >= numberMeshs)
223 {
224 error = "Incoerent model information.";
225 return false;
226 }
227
228 // streamming cost
229
230 float sqdiam = scale.LengthSquared();
231
232 ameshCostParam curCost = meshsCosts[mesh];
233 float mesh_streaming = streamingCost(curCost, sqdiam);
234
235 meshcostdata.model_streaming_cost += mesh_streaming;
236 meshcostdata.physics_cost += curCost.physicsCost;
237 }
238 else // instance as no mesh ??
239 {
240 // to do later if needed
241 meshcostdata.model_streaming_cost += 0.5f;
242 meshcostdata.physics_cost += 1.0f;
243 }
244
245 // assume unscripted and static prim server cost
246 meshcostdata.simulation_cost += 0.5f;
247 // charge for prims creation
248 meshsfee += primCreationCost;
249 }
250
251 if (skipedSmall > 0)
252 {
253 if (skipedSmall > numberInstances / 2)
254 {
255 error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() +
256 "m minimum allowed size. Please check scalling";
257 return false;
258 }
259 else
260 warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() +
261 " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() +
262 "m minimum allowed size. Please check scalling ";
263 }
264
265 if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost)
266 meshcostdata.resource_cost = meshcostdata.model_streaming_cost;
267 else
268 meshcostdata.resource_cost = meshcostdata.physics_cost;
269
270 if (meshcostdata.resource_cost < meshcostdata.simulation_cost)
271 meshcostdata.resource_cost = meshcostdata.simulation_cost;
272
273 // scale cost
274 // at this point a cost of 1.0 whould mean basic cost
275 meshsfee *= ModelMeshCostFactor;
276
277 if (meshsfee < ModelMinCostFactor)
278 meshsfee = ModelMinCostFactor;
279
280 // actually scale it to basic cost
281 meshsfee *= (float)basicCost;
282
283 meshsfee += 0.5f; // rounding
284
285 totalcost += (int)meshsfee;
286
287 // breakdown prices
288 // don't seem to be in use so removed code for now
289
290 return true;
291 }
292
293 // single mesh asset cost
294 private bool MeshCost(byte[] data, ameshCostParam cost,out bool skeleton, out bool avatarPhys, out string error)
295 {
296 cost.highLODSize = 0;
297 cost.medLODSize = 0;
298 cost.lowLODSize = 0;
299 cost.lowestLODSize = 0;
300 cost.physicsCost = 0.0f;
301 cost.costFee = 0.0f;
302
303 error = string.Empty;
304
305 skeleton = false;
306 avatarPhys = false;
307
308 if (data == null || data.Length == 0)
309 {
310 error = "Missing model information.";
311 return false;
312 }
313
314 OSD meshOsd = null;
315 int start = 0;
316
317 error = "Invalid model data";
318
319 using (MemoryStream ms = new MemoryStream(data))
320 {
321 try
322 {
323 OSD osd = OSDParser.DeserializeLLSDBinary(ms);
324 if (osd is OSDMap)
325 meshOsd = (OSDMap)osd;
326 else
327 return false;
328 }
329 catch (Exception e)
330 {
331 return false;
332 }
333 start = (int)ms.Position;
334 }
335
336 OSDMap map = (OSDMap)meshOsd;
337 OSDMap tmpmap;
338
339 int highlod_size = 0;
340 int medlod_size = 0;
341 int lowlod_size = 0;
342 int lowestlod_size = 0;
343 int skin_size = 0;
344
345 int hulls_size = 0;
346 int phys_nhulls;
347 int phys_hullsvertices = 0;
348
349 int physmesh_size = 0;
350 int phys_ntriangles = 0;
351
352 int submesh_offset = -1;
353
354 if (map.ContainsKey("skeleton"))
355 {
356 tmpmap = (OSDMap)map["skeleton"];
357 if (tmpmap.ContainsKey("offset") && tmpmap.ContainsKey("size"))
358 {
359 int sksize = tmpmap["size"].AsInteger();
360 if(sksize > 0)
361 skeleton = true;
362 }
363 }
364
365 if (map.ContainsKey("physics_convex"))
366 {
367 tmpmap = (OSDMap)map["physics_convex"];
368 if (tmpmap.ContainsKey("offset"))
369 submesh_offset = tmpmap["offset"].AsInteger() + start;
370 if (tmpmap.ContainsKey("size"))
371 hulls_size = tmpmap["size"].AsInteger();
372 }
373
374 if (submesh_offset < 0 || hulls_size == 0)
375 {
376 error = "Missing physics_convex block";
377 return false;
378 }
379
380 if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls))
381 {
382 error = "Bad physics_convex block";
383 return false;
384 }
385
386 submesh_offset = -1;
387
388 // only look for LOD meshs sizes
389
390 if (map.ContainsKey("high_lod"))
391 {
392 tmpmap = (OSDMap)map["high_lod"];
393 // see at least if there is a offset for this one
394 if (tmpmap.ContainsKey("offset"))
395 submesh_offset = tmpmap["offset"].AsInteger() + start;
396 if (tmpmap.ContainsKey("size"))
397 highlod_size = tmpmap["size"].AsInteger();
398 }
399
400 if (submesh_offset < 0 || highlod_size <= 0)
401 {
402 error = "Missing high_lod block";
403 return false;
404 }
405
406 bool haveprev = true;
407
408 if (map.ContainsKey("medium_lod"))
409 {
410 tmpmap = (OSDMap)map["medium_lod"];
411 if (tmpmap.ContainsKey("size"))
412 medlod_size = tmpmap["size"].AsInteger();
413 else
414 haveprev = false;
415 }
416
417 if (haveprev && map.ContainsKey("low_lod"))
418 {
419 tmpmap = (OSDMap)map["low_lod"];
420 if (tmpmap.ContainsKey("size"))
421 lowlod_size = tmpmap["size"].AsInteger();
422 else
423 haveprev = false;
424 }
425
426 if (haveprev && map.ContainsKey("lowest_lod"))
427 {
428 tmpmap = (OSDMap)map["lowest_lod"];
429 if (tmpmap.ContainsKey("size"))
430 lowestlod_size = tmpmap["size"].AsInteger();
431 }
432
433 if (map.ContainsKey("skin"))
434 {
435 tmpmap = (OSDMap)map["skin"];
436 if (tmpmap.ContainsKey("size"))
437 skin_size = tmpmap["size"].AsInteger();
438 }
439
440 cost.highLODSize = highlod_size;
441 cost.medLODSize = medlod_size;
442 cost.lowLODSize = lowlod_size;
443 cost.lowestLODSize = lowestlod_size;
444
445 submesh_offset = -1;
446
447 tmpmap = null;
448 if(map.ContainsKey("physics_mesh"))
449 tmpmap = (OSDMap)map["physics_mesh"];
450 else if (map.ContainsKey("physics_shape")) // old naming
451 tmpmap = (OSDMap)map["physics_shape"];
452
453 if(tmpmap != null)
454 {
455 if (tmpmap.ContainsKey("offset"))
456 submesh_offset = tmpmap["offset"].AsInteger() + start;
457 if (tmpmap.ContainsKey("size"))
458 physmesh_size = tmpmap["size"].AsInteger();
459
460 if (submesh_offset >= 0 || physmesh_size > 0)
461 {
462
463 if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles))
464 {
465 error = "Model data parsing error";
466 return false;
467 }
468 }
469 }
470
471 // upload is done in convex shape type so only one hull
472 phys_hullsvertices++;
473 cost.physicsCost = 0.04f * phys_hullsvertices;
474
475 float sfee;
476
477 sfee = data.Length; // start with total compressed data size
478
479 // penalize lod meshs that should be more builder optimized
480 sfee += medSizeWth * medlod_size;
481 sfee += lowSizeWth * lowlod_size;
482 sfee += lowestSizeWth * lowlod_size;
483
484 // physics
485 // favor potencial optimized meshs versus automatic decomposition
486 if (physmesh_size != 0)
487 sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull
488 else
489 sfee += physHullSizeWth * hulls_size;
490
491 // bytes to money
492 sfee *= bytecost;
493
494 cost.costFee = sfee;
495 return true;
496 }
497
498 // parses a LOD or physics mesh component
499 private bool submesh(byte[] data, int offset, int size, out int ntriangles)
500 {
501 ntriangles = 0;
502
503 OSD decodedMeshOsd = new OSD();
504 byte[] meshBytes = new byte[size];
505 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
506 try
507 {
508 using (MemoryStream inMs = new MemoryStream(meshBytes))
509 {
510 using (MemoryStream outMs = new MemoryStream())
511 {
512 using (ZOutputStream zOut = new ZOutputStream(outMs))
513 {
514 byte[] readBuffer = new byte[4096];
515 int readLen = 0;
516 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
517 {
518 zOut.Write(readBuffer, 0, readLen);
519 }
520 zOut.Flush();
521 outMs.Seek(0, SeekOrigin.Begin);
522
523 byte[] decompressedBuf = outMs.GetBuffer();
524 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
525 }
526 }
527 }
528 }
529 catch (Exception e)
530 {
531 return false;
532 }
533
534 OSDArray decodedMeshOsdArray = null;
535 if ((!decodedMeshOsd is OSDArray))
536 return false;
537
538 byte[] dummy;
539
540 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
541 foreach (OSD subMeshOsd in decodedMeshOsdArray)
542 {
543 if (subMeshOsd is OSDMap)
544 {
545 OSDMap subtmpmap = (OSDMap)subMeshOsd;
546 if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"]))
547 continue;
548
549 if (!subtmpmap.ContainsKey("Position"))
550 return false;
551
552 if (subtmpmap.ContainsKey("TriangleList"))
553 {
554 dummy = subtmpmap["TriangleList"].AsBinary();
555 ntriangles += dummy.Length / bytesPerCoord;
556 }
557 else
558 return false;
559 }
560 }
561
562 return true;
563 }
564
565 // parses convex hulls component
566 private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls)
567 {
568 nvertices = 0;
569 nhulls = 1;
570
571 OSD decodedMeshOsd = new OSD();
572 byte[] meshBytes = new byte[size];
573 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
574 try
575 {
576 using (MemoryStream inMs = new MemoryStream(meshBytes))
577 {
578 using (MemoryStream outMs = new MemoryStream())
579 {
580 using (ZOutputStream zOut = new ZOutputStream(outMs))
581 {
582 byte[] readBuffer = new byte[4096];
583 int readLen = 0;
584 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
585 {
586 zOut.Write(readBuffer, 0, readLen);
587 }
588 zOut.Flush();
589 outMs.Seek(0, SeekOrigin.Begin);
590
591 byte[] decompressedBuf = outMs.GetBuffer();
592 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
593 }
594 }
595 }
596 }
597 catch (Exception e)
598 {
599 return false;
600 }
601
602 OSDMap cmap = (OSDMap)decodedMeshOsd;
603 if (cmap == null)
604 return false;
605
606 byte[] dummy;
607
608 // must have one of this
609 if (cmap.ContainsKey("BoundingVerts"))
610 {
611 dummy = cmap["BoundingVerts"].AsBinary();
612 nvertices = dummy.Length / bytesPerCoord;
613 }
614 else
615 return false;
616
617/* upload is done with convex shape type
618 if (cmap.ContainsKey("HullList"))
619 {
620 dummy = cmap["HullList"].AsBinary();
621 nhulls += dummy.Length;
622 }
623
624
625 if (cmap.ContainsKey("Positions"))
626 {
627 dummy = cmap["Positions"].AsBinary();
628 nvertices = dummy.Length / bytesPerCoord;
629 }
630 */
631
632 return true;
633 }
634
635 // returns streaming cost from on mesh LODs sizes in curCost and square of prim size length
636 private float streamingCost(ameshCostParam curCost, float sqdiam)
637 {
638 // compute efective areas
639 float ma = 262144f;
640
641 float mh = sqdiam * highLodFactor;
642 if (mh > ma)
643 mh = ma;
644 float mm = sqdiam * midLodFactor;
645 if (mm > ma)
646 mm = ma;
647
648 float ml = sqdiam * lowLodFactor;
649 if (ml > ma)
650 ml = ma;
651
652 float mlst = ma;
653
654 mlst -= ml;
655 ml -= mm;
656 mm -= mh;
657
658 if (mlst < 1.0f)
659 mlst = 1.0f;
660 if (ml < 1.0f)
661 ml = 1.0f;
662 if (mm < 1.0f)
663 mm = 1.0f;
664 if (mh < 1.0f)
665 mh = 1.0f;
666
667 ma = mlst + ml + mm + mh;
668
669 // get LODs compressed sizes
670 // giving 384 bytes bonus
671 int lst = curCost.lowestLODSize - 384;
672 int l = curCost.lowLODSize - 384;
673 int m = curCost.medLODSize - 384;
674 int h = curCost.highLODSize - 384;
675
676 // use previus higher LOD size on missing ones
677 if (m <= 0)
678 m = h;
679 if (l <= 0)
680 l = m;
681 if (lst <= 0)
682 lst = l;
683
684 // force minumum sizes
685 if (lst < 16)
686 lst = 16;
687 if (l < 16)
688 l = 16;
689 if (m < 16)
690 m = 16;
691 if (h < 16)
692 h = 16;
693
694 // compute cost weighted by relative effective areas
695 float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh;
696 cost /= ma;
697
698 cost *= 0.004f; // overall tunning parameter
699
700 return cost;
701 }
702 }
703}