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.cs671
1 files changed, 671 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..4a3fae6
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
@@ -0,0 +1,671 @@
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 public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost,
100 LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning)
101 {
102 totalcost = 0;
103 error = string.Empty;
104
105 if (resources == null ||
106 resources.instance_list == null ||
107 resources.instance_list.Array.Count == 0)
108 {
109 error = "missing model information.";
110 return false;
111 }
112
113 int numberInstances = resources.instance_list.Array.Count;
114
115 if( numberInstances > ObjectLinkedPartsMax )
116 {
117 error = "Model whould have more than " + ObjectLinkedPartsMax.ToString() + " linked prims";
118 return false;
119 }
120
121 meshcostdata.model_streaming_cost = 0.0;
122 meshcostdata.simulation_cost = 0.0;
123 meshcostdata.physics_cost = 0.0;
124 meshcostdata.resource_cost = 0.0;
125
126 meshcostdata.upload_price_breakdown.mesh_instance = 0;
127 meshcostdata.upload_price_breakdown.mesh_physics = 0;
128 meshcostdata.upload_price_breakdown.mesh_streaming = 0;
129 meshcostdata.upload_price_breakdown.model = 0;
130
131 int itmp;
132
133 // textures cost
134 if (resources.texture_list != null && resources.texture_list.Array.Count > 0)
135 {
136 float textures_cost = (float)(resources.texture_list.Array.Count * basicCost);
137 textures_cost *= ModelTextureCostFactor;
138
139 itmp = (int)(textures_cost + 0.5f); // round
140 meshcostdata.upload_price_breakdown.texture = itmp;
141 totalcost += itmp;
142 }
143
144 // meshs assets cost
145 float meshsfee = 0;
146 int numberMeshs = 0;
147 bool haveMeshs = false;
148 List<ameshCostParam> meshsCosts = new List<ameshCostParam>();
149
150 if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0)
151 {
152 numberMeshs = resources.mesh_list.Array.Count;
153
154 for (int i = 0; i < numberMeshs; i++)
155 {
156 ameshCostParam curCost = new ameshCostParam();
157 byte[] data = (byte[])resources.mesh_list.Array[i];
158
159 if (!MeshCost(data, curCost, out error))
160 {
161 return false;
162 }
163 meshsCosts.Add(curCost);
164 meshsfee += curCost.costFee;
165 }
166 haveMeshs = true;
167 }
168
169 // instances (prims) cost
170
171
172 int mesh;
173 int skipedSmall = 0;
174 for (int i = 0; i < numberInstances; i++)
175 {
176 Hashtable inst = (Hashtable)resources.instance_list.Array[i];
177
178 ArrayList ascale = (ArrayList)inst["scale"];
179 Vector3 scale;
180 double tmp;
181 tmp = (double)ascale[0];
182 scale.X = (float)tmp;
183 tmp = (double)ascale[1];
184 scale.Y = (float)tmp;
185 tmp = (double)ascale[2];
186 scale.Z = (float)tmp;
187
188 if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin)
189 {
190 skipedSmall++;
191 continue;
192 }
193
194 if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax)
195 {
196 error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale";
197 return false;
198 }
199
200 if (haveMeshs && inst.ContainsKey("mesh"))
201 {
202 mesh = (int)inst["mesh"];
203
204 if (mesh >= numberMeshs)
205 {
206 error = "Incoerent model information.";
207 return false;
208 }
209
210 // streamming cost
211
212 float sqdiam = scale.LengthSquared();
213
214 ameshCostParam curCost = meshsCosts[mesh];
215 float mesh_streaming = streamingCost(curCost, sqdiam);
216
217 meshcostdata.model_streaming_cost += mesh_streaming;
218 meshcostdata.physics_cost += curCost.physicsCost;
219 }
220 else // instance as no mesh ??
221 {
222 // to do later if needed
223 meshcostdata.model_streaming_cost += 0.5f;
224 meshcostdata.physics_cost += 1.0f;
225 }
226
227 // assume unscripted and static prim server cost
228 meshcostdata.simulation_cost += 0.5f;
229 // charge for prims creation
230 meshsfee += primCreationCost;
231 }
232
233 if (skipedSmall > 0)
234 {
235 if (skipedSmall > numberInstances / 2)
236 {
237 error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() +
238 "m minimum allowed size. Please check scalling";
239 return false;
240 }
241 else
242 warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() +
243 " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() +
244 "m minimum allowed size. Please check scalling ";
245 }
246
247 if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost)
248 meshcostdata.resource_cost = meshcostdata.model_streaming_cost;
249 else
250 meshcostdata.resource_cost = meshcostdata.physics_cost;
251
252 if (meshcostdata.resource_cost < meshcostdata.simulation_cost)
253 meshcostdata.resource_cost = meshcostdata.simulation_cost;
254
255 // scale cost
256 // at this point a cost of 1.0 whould mean basic cost
257 meshsfee *= ModelMeshCostFactor;
258
259 if (meshsfee < ModelMinCostFactor)
260 meshsfee = ModelMinCostFactor;
261
262 // actually scale it to basic cost
263 meshsfee *= (float)basicCost;
264
265 meshsfee += 0.5f; // rounding
266
267 totalcost += (int)meshsfee;
268
269 // breakdown prices
270 // don't seem to be in use so removed code for now
271
272 return true;
273 }
274
275 // single mesh asset cost
276 private bool MeshCost(byte[] data, ameshCostParam cost, out string error)
277 {
278 cost.highLODSize = 0;
279 cost.medLODSize = 0;
280 cost.lowLODSize = 0;
281 cost.lowestLODSize = 0;
282 cost.physicsCost = 0.0f;
283 cost.costFee = 0.0f;
284
285 error = string.Empty;
286
287 if (data == null || data.Length == 0)
288 {
289 error = "Missing model information.";
290 return false;
291 }
292
293 OSD meshOsd = null;
294 int start = 0;
295
296 error = "Invalid model data";
297
298 using (MemoryStream ms = new MemoryStream(data))
299 {
300 try
301 {
302 OSD osd = OSDParser.DeserializeLLSDBinary(ms);
303 if (osd is OSDMap)
304 meshOsd = (OSDMap)osd;
305 else
306 return false;
307 }
308 catch (Exception e)
309 {
310 return false;
311 }
312 start = (int)ms.Position;
313 }
314
315 OSDMap map = (OSDMap)meshOsd;
316 OSDMap tmpmap;
317
318 int highlod_size = 0;
319 int medlod_size = 0;
320 int lowlod_size = 0;
321 int lowestlod_size = 0;
322 int skin_size = 0;
323
324 int hulls_size = 0;
325 int phys_nhulls;
326 int phys_hullsvertices = 0;
327
328 int physmesh_size = 0;
329 int phys_ntriangles = 0;
330
331 int submesh_offset = -1;
332
333 if (map.ContainsKey("physics_convex"))
334 {
335 tmpmap = (OSDMap)map["physics_convex"];
336 if (tmpmap.ContainsKey("offset"))
337 submesh_offset = tmpmap["offset"].AsInteger() + start;
338 if (tmpmap.ContainsKey("size"))
339 hulls_size = tmpmap["size"].AsInteger();
340 }
341
342 if (submesh_offset < 0 || hulls_size == 0)
343 {
344 error = "Missing physics_convex block";
345 return false;
346 }
347
348 if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls))
349 {
350 error = "Bad physics_convex block";
351 return false;
352 }
353
354 submesh_offset = -1;
355
356 // only look for LOD meshs sizes
357
358 if (map.ContainsKey("high_lod"))
359 {
360 tmpmap = (OSDMap)map["high_lod"];
361 // see at least if there is a offset for this one
362 if (tmpmap.ContainsKey("offset"))
363 submesh_offset = tmpmap["offset"].AsInteger() + start;
364 if (tmpmap.ContainsKey("size"))
365 highlod_size = tmpmap["size"].AsInteger();
366 }
367
368 if (submesh_offset < 0 || highlod_size <= 0)
369 {
370 error = "Missing high_lod block";
371 return false;
372 }
373
374 bool haveprev = true;
375
376 if (map.ContainsKey("medium_lod"))
377 {
378 tmpmap = (OSDMap)map["medium_lod"];
379 if (tmpmap.ContainsKey("size"))
380 medlod_size = tmpmap["size"].AsInteger();
381 else
382 haveprev = false;
383 }
384
385 if (haveprev && map.ContainsKey("low_lod"))
386 {
387 tmpmap = (OSDMap)map["low_lod"];
388 if (tmpmap.ContainsKey("size"))
389 lowlod_size = tmpmap["size"].AsInteger();
390 else
391 haveprev = false;
392 }
393
394 if (haveprev && map.ContainsKey("lowest_lod"))
395 {
396 tmpmap = (OSDMap)map["lowest_lod"];
397 if (tmpmap.ContainsKey("size"))
398 lowestlod_size = tmpmap["size"].AsInteger();
399 }
400
401 if (map.ContainsKey("skin"))
402 {
403 tmpmap = (OSDMap)map["skin"];
404 if (tmpmap.ContainsKey("size"))
405 skin_size = tmpmap["size"].AsInteger();
406 }
407
408 cost.highLODSize = highlod_size;
409 cost.medLODSize = medlod_size;
410 cost.lowLODSize = lowlod_size;
411 cost.lowestLODSize = lowestlod_size;
412
413 submesh_offset = -1;
414
415 tmpmap = null;
416 if(map.ContainsKey("physics_mesh"))
417 tmpmap = (OSDMap)map["physics_mesh"];
418 else if (map.ContainsKey("physics_shape")) // old naming
419 tmpmap = (OSDMap)map["physics_shape"];
420
421 if(tmpmap != null)
422 {
423 if (tmpmap.ContainsKey("offset"))
424 submesh_offset = tmpmap["offset"].AsInteger() + start;
425 if (tmpmap.ContainsKey("size"))
426 physmesh_size = tmpmap["size"].AsInteger();
427
428 if (submesh_offset >= 0 || physmesh_size > 0)
429 {
430
431 if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles))
432 {
433 error = "Model data parsing error";
434 return false;
435 }
436 }
437 }
438
439 // upload is done in convex shape type so only one hull
440 phys_hullsvertices++;
441 cost.physicsCost = 0.04f * phys_hullsvertices;
442
443 float sfee;
444
445 sfee = data.Length; // start with total compressed data size
446
447 // penalize lod meshs that should be more builder optimized
448 sfee += medSizeWth * medlod_size;
449 sfee += lowSizeWth * lowlod_size;
450 sfee += lowestSizeWth * lowlod_size;
451
452 // physics
453 // favor potencial optimized meshs versus automatic decomposition
454 if (physmesh_size != 0)
455 sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull
456 else
457 sfee += physHullSizeWth * hulls_size;
458
459 // bytes to money
460 sfee *= bytecost;
461
462 cost.costFee = sfee;
463 return true;
464 }
465
466 // parses a LOD or physics mesh component
467 private bool submesh(byte[] data, int offset, int size, out int ntriangles)
468 {
469 ntriangles = 0;
470
471 OSD decodedMeshOsd = new OSD();
472 byte[] meshBytes = new byte[size];
473 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
474 try
475 {
476 using (MemoryStream inMs = new MemoryStream(meshBytes))
477 {
478 using (MemoryStream outMs = new MemoryStream())
479 {
480 using (ZOutputStream zOut = new ZOutputStream(outMs))
481 {
482 byte[] readBuffer = new byte[4096];
483 int readLen = 0;
484 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
485 {
486 zOut.Write(readBuffer, 0, readLen);
487 }
488 zOut.Flush();
489 outMs.Seek(0, SeekOrigin.Begin);
490
491 byte[] decompressedBuf = outMs.GetBuffer();
492 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
493 }
494 }
495 }
496 }
497 catch (Exception e)
498 {
499 return false;
500 }
501
502 OSDArray decodedMeshOsdArray = null;
503 if ((!decodedMeshOsd is OSDArray))
504 return false;
505
506 byte[] dummy;
507
508 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
509 foreach (OSD subMeshOsd in decodedMeshOsdArray)
510 {
511 if (subMeshOsd is OSDMap)
512 {
513 OSDMap subtmpmap = (OSDMap)subMeshOsd;
514 if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"]))
515 continue;
516
517 if (!subtmpmap.ContainsKey("Position"))
518 return false;
519
520 if (subtmpmap.ContainsKey("TriangleList"))
521 {
522 dummy = subtmpmap["TriangleList"].AsBinary();
523 ntriangles += dummy.Length / bytesPerCoord;
524 }
525 else
526 return false;
527 }
528 }
529
530 return true;
531 }
532
533 // parses convex hulls component
534 private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls)
535 {
536 nvertices = 0;
537 nhulls = 1;
538
539 OSD decodedMeshOsd = new OSD();
540 byte[] meshBytes = new byte[size];
541 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
542 try
543 {
544 using (MemoryStream inMs = new MemoryStream(meshBytes))
545 {
546 using (MemoryStream outMs = new MemoryStream())
547 {
548 using (ZOutputStream zOut = new ZOutputStream(outMs))
549 {
550 byte[] readBuffer = new byte[4096];
551 int readLen = 0;
552 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
553 {
554 zOut.Write(readBuffer, 0, readLen);
555 }
556 zOut.Flush();
557 outMs.Seek(0, SeekOrigin.Begin);
558
559 byte[] decompressedBuf = outMs.GetBuffer();
560 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
561 }
562 }
563 }
564 }
565 catch (Exception e)
566 {
567 return false;
568 }
569
570 OSDMap cmap = (OSDMap)decodedMeshOsd;
571 if (cmap == null)
572 return false;
573
574 byte[] dummy;
575
576 // must have one of this
577 if (cmap.ContainsKey("BoundingVerts"))
578 {
579 dummy = cmap["BoundingVerts"].AsBinary();
580 nvertices = dummy.Length / bytesPerCoord;
581 }
582 else
583 return false;
584
585/* upload is done with convex shape type
586 if (cmap.ContainsKey("HullList"))
587 {
588 dummy = cmap["HullList"].AsBinary();
589 nhulls += dummy.Length;
590 }
591
592
593 if (cmap.ContainsKey("Positions"))
594 {
595 dummy = cmap["Positions"].AsBinary();
596 nvertices = dummy.Length / bytesPerCoord;
597 }
598 */
599
600 return true;
601 }
602
603 // returns streaming cost from on mesh LODs sizes in curCost and square of prim size length
604 private float streamingCost(ameshCostParam curCost, float sqdiam)
605 {
606 // compute efective areas
607 float ma = 262144f;
608
609 float mh = sqdiam * highLodFactor;
610 if (mh > ma)
611 mh = ma;
612 float mm = sqdiam * midLodFactor;
613 if (mm > ma)
614 mm = ma;
615
616 float ml = sqdiam * lowLodFactor;
617 if (ml > ma)
618 ml = ma;
619
620 float mlst = ma;
621
622 mlst -= ml;
623 ml -= mm;
624 mm -= mh;
625
626 if (mlst < 1.0f)
627 mlst = 1.0f;
628 if (ml < 1.0f)
629 ml = 1.0f;
630 if (mm < 1.0f)
631 mm = 1.0f;
632 if (mh < 1.0f)
633 mh = 1.0f;
634
635 ma = mlst + ml + mm + mh;
636
637 // get LODs compressed sizes
638 // giving 384 bytes bonus
639 int lst = curCost.lowestLODSize - 384;
640 int l = curCost.lowLODSize - 384;
641 int m = curCost.medLODSize - 384;
642 int h = curCost.highLODSize - 384;
643
644 // use previus higher LOD size on missing ones
645 if (m <= 0)
646 m = h;
647 if (l <= 0)
648 l = m;
649 if (lst <= 0)
650 lst = l;
651
652 // force minumum sizes
653 if (lst < 16)
654 lst = 16;
655 if (l < 16)
656 l = 16;
657 if (m < 16)
658 m = 16;
659 if (h < 16)
660 h = 16;
661
662 // compute cost weighted by relative effective areas
663 float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh;
664 cost /= ma;
665
666 cost *= 0.004f; // overall tunning parameter
667
668 return cost;
669 }
670 }
671}