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