aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs596
1 files changed, 596 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..8adacab
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
@@ -0,0 +1,596 @@
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 class ModelCost
27 {
28 float ModelMinCost = 5.0f; // try to favor small meshs versus sculpts
29
30 // scale prices relative to basic cost
31 const float ModelCostScale = 1.0f;
32
33 const float primCreationCost = 0.01f; // 256 prims cost extra 2.56
34
35 // weigthed size to money convertion
36 const float bytecost = 1e-4f;
37
38 // for mesh upload fees based on compressed data sizes
39 // not using streaming physics and server costs as SL apparently does ??
40
41 const float medSizeWth = 1f; // 2x
42 const float lowSizeWth = 1.5f; // 2.5x
43 const float lowestSizeWth = 2f; // 3x
44 // favor potencial optimized meshs versus automatic decomposition
45 const float physMeshSizeWth = 6f; // counts 7x
46 const float physHullSizeWth = 8f; // counts 9x
47
48 // price compression to promote complex meshs
49 const float feeCompressionBase = 50.0f; // transition from linear to log cost
50 const float feeCompressionScale = 250.0f; // 10000 scales to 1000
51
52 // stream cost size factors
53 const float highLodFactor = 17.36f;
54 const float midLodFactor = 277.78f;
55 const float lowLodFactor = 1111.11f;
56
57 const int bytesPerCoord = 6; // 3 coords, 2 bytes per each
58
59 private class ameshCostParam
60 {
61 public int highLODSize;
62 public int medLODSize;
63 public int lowLODSize;
64 public int lowestLODSize;
65 public float costFee;
66 public float physicsCost;
67 }
68
69 public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, LLSDAssetUploadResponseData meshcostdata, out string error)
70 {
71 totalcost = 0;
72 error = string.Empty;
73
74 if (resources == null ||
75 resources.instance_list == null ||
76 resources.instance_list.Array.Count == 0)
77 {
78 error = "Unable to upload mesh model. missing information.";
79 return false;
80 }
81
82 meshcostdata.model_streaming_cost = 0.0;
83 meshcostdata.simulation_cost = 0.0;
84 meshcostdata.physics_cost = 0.0;
85 meshcostdata.resource_cost = 0.0;
86
87 meshcostdata.upload_price_breakdown.mesh_instance = 0;
88 meshcostdata.upload_price_breakdown.mesh_physics = 0;
89 meshcostdata.upload_price_breakdown.mesh_streaming = 0;
90 meshcostdata.upload_price_breakdown.model = 0;
91
92 int itmp;
93
94 // textures cost
95 if (resources.texture_list != null && resources.texture_list.Array.Count > 0)
96 {
97 int textures_cost = resources.texture_list.Array.Count;
98 textures_cost *= basicCost;
99
100 meshcostdata.upload_price_breakdown.texture = textures_cost;
101 totalcost += textures_cost;
102 }
103
104 float meshsfee = 0;
105
106 // meshs assets cost
107
108 int numberMeshs = 0;
109 List<ameshCostParam> meshsCosts = new List<ameshCostParam>();
110 // a model could have no mesh actually
111 if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0)
112 {
113 numberMeshs = resources.mesh_list.Array.Count;
114
115 for (int i = 0; i < numberMeshs; i++)
116 {
117 ameshCostParam curCost = new ameshCostParam();
118 byte[] data = (byte[])resources.mesh_list.Array[i];
119
120 if (!MeshCost(data, curCost, out error))
121 {
122 return false;
123 }
124 meshsCosts.Add(curCost);
125 meshsfee += curCost.costFee;
126 }
127 }
128
129 // instances (prims) cost
130 int numberInstances = resources.instance_list.Array.Count;
131 int mesh;
132 for (int i = 0; i < numberInstances; i++)
133 {
134 Hashtable inst = (Hashtable)resources.instance_list.Array[i];
135
136 // streamming cost
137 // assume all instances have a mesh
138 // but in general they can have normal prims
139 // but for now that seems not suported
140 // when they do, we will need to inspect pbs information
141 // and have cost funtions for all prims types
142 // don't check for shape type none, since
143 // that could be used to upload meshs with low cost
144 // changing later inworld
145
146 ArrayList ascale = (ArrayList)inst["scale"];
147 Vector3 scale;
148 double tmp;
149 tmp = (double)ascale[0];
150 scale.X = (float)tmp;
151 tmp = (double)ascale[1];
152 scale.Y = (float)tmp;
153 tmp = (double)ascale[2];
154 scale.Z = (float)tmp;
155
156 float sqdiam = scale.LengthSquared();
157
158 mesh = (int)inst["mesh"];
159
160 if(mesh >= numberMeshs)
161 {
162 error = "Unable to upload mesh model. incoerent information.";
163 return false;
164 }
165
166 ameshCostParam curCost = meshsCosts[mesh];
167 float mesh_streaming = streamingCost(curCost, sqdiam);
168
169 meshcostdata.model_streaming_cost += mesh_streaming;
170
171 meshcostdata.physics_cost += curCost.physicsCost;
172
173 // unscripted and static prim server cost
174 meshcostdata.simulation_cost += 0.5f;
175 // charge for prims creation
176 meshsfee += primCreationCost;
177 }
178
179 if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost)
180 meshcostdata.resource_cost = meshcostdata.model_streaming_cost;
181 else
182 meshcostdata.resource_cost = meshcostdata.physics_cost;
183
184 if (meshsfee < ModelMinCost)
185 meshsfee = ModelMinCost;
186
187 meshsfee *= ModelCostScale;
188 meshsfee += 0.5f; // rounding
189
190 totalcost += (int)meshsfee;
191
192 // breakdown prices
193 // don't seem to be in use so removed code for now
194
195 return true;
196 }
197
198 private bool MeshCost(byte[] data, ameshCostParam cost, out string error)
199 {
200 cost.highLODSize = 0;
201 cost.medLODSize = 0;
202 cost.lowLODSize = 0;
203 cost.lowestLODSize = 0;
204 cost.physicsCost = 0.0f;
205 cost.costFee = 0.0f;
206
207 error = string.Empty;
208
209 if (data == null || data.Length == 0)
210 {
211 error = "Unable to upload mesh model. missing information.";
212 return false;
213 }
214
215 OSD meshOsd = null;
216 int start = 0;
217
218 error = "Unable to upload mesh model. Invalid data";
219
220 using (MemoryStream ms = new MemoryStream(data))
221 {
222 try
223 {
224 OSD osd = OSDParser.DeserializeLLSDBinary(ms);
225 if (osd is OSDMap)
226 meshOsd = (OSDMap)osd;
227 else
228 return false;
229 }
230 catch (Exception e)
231 {
232 return false;
233 }
234 start = (int)ms.Position;
235 }
236
237 OSDMap map = (OSDMap)meshOsd;
238 OSDMap tmpmap;
239
240 int highlod_size = 0;
241 int medlod_size = 0;
242 int lowlod_size = 0;
243 int lowestlod_size = 0;
244 int skin_size = 0;
245
246 int hulls_size = 0;
247 int phys_nhulls;
248 int phys_hullsvertices = 0;
249
250 int physmesh_size = 0;
251 int phys_ntriangles = 0;
252
253 int submesh_offset = -1;
254
255 if (map.ContainsKey("physics_convex"))
256 {
257 tmpmap = (OSDMap)map["physics_convex"];
258 if (tmpmap.ContainsKey("offset"))
259 submesh_offset = tmpmap["offset"].AsInteger() + start;
260 if (tmpmap.ContainsKey("size"))
261 hulls_size = tmpmap["size"].AsInteger();
262 }
263
264 if (submesh_offset < 0 || hulls_size == 0)
265 {
266 error = "Unable to upload mesh model. missing physics_convex block";
267 return false;
268 }
269
270 if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls))
271 {
272 error = "Unable to upload mesh model. bad physics_convex block";
273 return false;
274 }
275
276 submesh_offset = -1;
277
278 // only look for LOD meshs sizes
279
280 if (map.ContainsKey("high_lod"))
281 {
282 tmpmap = (OSDMap)map["high_lod"];
283 // see at least if there is a offset for this one
284 if (tmpmap.ContainsKey("offset"))
285 submesh_offset = tmpmap["offset"].AsInteger() + start;
286 if (tmpmap.ContainsKey("size"))
287 highlod_size = tmpmap["size"].AsInteger();
288 }
289
290 if (submesh_offset < 0 || highlod_size <= 0)
291 {
292 error = "Unable to upload mesh model. missing high_lod";
293 return false;
294 }
295
296 bool haveprev = true;
297
298 if (map.ContainsKey("medium_lod"))
299 {
300 tmpmap = (OSDMap)map["medium_lod"];
301 if (tmpmap.ContainsKey("size"))
302 medlod_size = tmpmap["size"].AsInteger();
303 else
304 haveprev = false;
305 }
306
307 if (haveprev && map.ContainsKey("low_lod"))
308 {
309 tmpmap = (OSDMap)map["low_lod"];
310 if (tmpmap.ContainsKey("size"))
311 lowlod_size = tmpmap["size"].AsInteger();
312 else
313 haveprev = false;
314 }
315
316 if (haveprev && map.ContainsKey("lowest_lod"))
317 {
318 tmpmap = (OSDMap)map["lowest_lod"];
319 if (tmpmap.ContainsKey("size"))
320 lowestlod_size = tmpmap["size"].AsInteger();
321 }
322
323 if (map.ContainsKey("skin"))
324 {
325 tmpmap = (OSDMap)map["skin"];
326 if (tmpmap.ContainsKey("size"))
327 skin_size = tmpmap["size"].AsInteger();
328 }
329
330 cost.highLODSize = highlod_size;
331 cost.medLODSize = medlod_size;
332 cost.lowLODSize = lowlod_size;
333 cost.lowestLODSize = lowestlod_size;
334
335 submesh_offset = -1;
336
337 if (map.ContainsKey("physics_mesh"))
338 {
339 tmpmap = (OSDMap)map["physics_mesh"];
340 if (tmpmap.ContainsKey("offset"))
341 submesh_offset = tmpmap["offset"].AsInteger() + start;
342 if (tmpmap.ContainsKey("size"))
343 physmesh_size = tmpmap["size"].AsInteger();
344
345 if (submesh_offset >= 0 || physmesh_size > 0)
346 {
347
348 if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles))
349 {
350 error = "Unable to upload mesh model. parsing error";
351 return false;
352 }
353 }
354 }
355
356 // upload is done in convex shape type so only one hull
357 phys_hullsvertices++;
358 cost.physicsCost = 0.04f * phys_hullsvertices;
359
360 float sfee;
361
362 sfee = data.Length; // start with total compressed data size
363
364 // penalize lod meshs that should be more builder optimized
365 sfee += medSizeWth * medlod_size;
366 sfee += lowSizeWth * lowlod_size;
367 sfee += lowestSizeWth * lowlod_size;
368
369 // physics
370 // favor potencial optimized meshs versus automatic decomposition
371 if (physmesh_size != 0)
372 sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull
373 else
374 sfee += physHullSizeWth * hulls_size;
375
376 // bytes to money
377 sfee *= bytecost;
378
379 // fee compression
380 if (sfee > feeCompressionBase)
381 {
382 sfee -= feeCompressionBase;
383 sfee = feeCompressionScale * (float)Math.Log10((double)sfee);
384 sfee += feeCompressionBase;
385 }
386
387
388
389 cost.costFee = sfee;
390 return true;
391 }
392
393 private bool submesh(byte[] data, int offset, int size, out int ntriangles)
394 {
395 ntriangles = 0;
396
397 OSD decodedMeshOsd = new OSD();
398 byte[] meshBytes = new byte[size];
399 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
400 try
401 {
402 using (MemoryStream inMs = new MemoryStream(meshBytes))
403 {
404 using (MemoryStream outMs = new MemoryStream())
405 {
406 using (ZOutputStream zOut = new ZOutputStream(outMs))
407 {
408 byte[] readBuffer = new byte[4096];
409 int readLen = 0;
410 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
411 {
412 zOut.Write(readBuffer, 0, readLen);
413 }
414 zOut.Flush();
415 outMs.Seek(0, SeekOrigin.Begin);
416
417 byte[] decompressedBuf = outMs.GetBuffer();
418 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
419 }
420 }
421 }
422 }
423 catch (Exception e)
424 {
425 return false;
426 }
427
428 OSDArray decodedMeshOsdArray = null;
429 if ((!decodedMeshOsd is OSDArray))
430 return false;
431
432 byte[] dummy;
433
434 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
435 foreach (OSD subMeshOsd in decodedMeshOsdArray)
436 {
437 if (subMeshOsd is OSDMap)
438 {
439 OSDMap subtmpmap = (OSDMap)subMeshOsd;
440 if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"]))
441 continue;
442
443 if (!subtmpmap.ContainsKey("Position"))
444 return false;
445
446 if (subtmpmap.ContainsKey("TriangleList"))
447 {
448 dummy = subtmpmap["TriangleList"].AsBinary();
449 ntriangles += dummy.Length / bytesPerCoord;
450 }
451 else
452 return false;
453 }
454 }
455
456 return true;
457 }
458
459 private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls)
460 {
461 nvertices = 0;
462 nhulls = 1;
463
464 OSD decodedMeshOsd = new OSD();
465 byte[] meshBytes = new byte[size];
466 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
467 try
468 {
469 using (MemoryStream inMs = new MemoryStream(meshBytes))
470 {
471 using (MemoryStream outMs = new MemoryStream())
472 {
473 using (ZOutputStream zOut = new ZOutputStream(outMs))
474 {
475 byte[] readBuffer = new byte[4096];
476 int readLen = 0;
477 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
478 {
479 zOut.Write(readBuffer, 0, readLen);
480 }
481 zOut.Flush();
482 outMs.Seek(0, SeekOrigin.Begin);
483
484 byte[] decompressedBuf = outMs.GetBuffer();
485 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
486 }
487 }
488 }
489 }
490 catch (Exception e)
491 {
492 return false;
493 }
494
495 OSDMap cmap = (OSDMap)decodedMeshOsd;
496 if (cmap == null)
497 return false;
498
499 byte[] dummy;
500
501 // must have one of this
502 if (cmap.ContainsKey("BoundingVerts"))
503 {
504 dummy = cmap["BoundingVerts"].AsBinary();
505 nvertices = dummy.Length / bytesPerCoord;
506 }
507 else
508 return false;
509
510/* upload is done with convex shape type
511 if (cmap.ContainsKey("HullList"))
512 {
513 dummy = cmap["HullList"].AsBinary();
514 nhulls += dummy.Length;
515 }
516
517
518 if (cmap.ContainsKey("Positions"))
519 {
520 dummy = cmap["Positions"].AsBinary();
521 nvertices = dummy.Length / bytesPerCoord;
522 }
523 */
524
525 return true;
526 }
527
528 private float streamingCost(ameshCostParam curCost, float sqdiam)
529 {
530 // compute efective areas
531 float ma = 262144f;
532
533 float mh = sqdiam * highLodFactor;
534 if (mh > ma)
535 mh = ma;
536 float mm = sqdiam * midLodFactor;
537 if (mm > ma)
538 mm = ma;
539
540 float ml = sqdiam * lowLodFactor;
541 if (ml > ma)
542 ml = ma;
543
544 float mlst = ma;
545
546 mlst -= ml;
547 ml -= mm;
548 mm -= mh;
549
550 if (mlst < 1.0f)
551 mlst = 1.0f;
552 if (ml < 1.0f)
553 ml = 1.0f;
554 if (mm < 1.0f)
555 mm = 1.0f;
556 if (mh < 1.0f)
557 mh = 1.0f;
558
559 ma = mlst + ml + mm + mh;
560
561 // get LODs compressed sizes
562 // giving 384 bytes bonus
563 int lst = curCost.lowestLODSize - 384;
564 int l = curCost.lowLODSize - 384;
565 int m = curCost.medLODSize - 384;
566 int h = curCost.highLODSize - 384;
567
568 // use previus higher LOD size on missing ones
569 if (m <= 0)
570 m = h;
571 if (l <= 0)
572 l = m;
573 if (lst <= 0)
574 lst = l;
575
576 // force minumum sizes
577 if (lst < 16)
578 lst = 16;
579 if (l < 16)
580 l = 16;
581 if (m < 16)
582 m = 16;
583 if (h < 16)
584 h = 16;
585
586 // compute cost weighted by relative effective areas
587
588 float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh;
589 cost /= ma;
590
591 cost *= 0.004f; // overall tunning parameter
592
593 return cost;
594 }
595 }
596}