diff options
author | onefang | 2019-09-11 16:36:50 +1000 |
---|---|---|
committer | onefang | 2019-09-11 16:36:50 +1000 |
commit | 50cd1ffd32f69228e566f2b0b89f86ea0d9fe489 (patch) | |
tree | 52f2ab0c04f1a5d7d6ac5dc872981b4b156447e7 /OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs | |
parent | Renamed branch to SledjChisl. (diff) | |
parent | Bump to release flavour, build 0. (diff) | |
download | opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.zip opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.gz opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.bz2 opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.xz |
Merge branch 'SledjChisl'
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs | 746 |
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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Text; | ||
34 | |||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | |||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Region.Framework; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | using OpenSim.Framework.Capabilities; | ||
42 | |||
43 | using ComponentAce.Compression.Libs.zlib; | ||
44 | |||
45 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; | ||
46 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; | ||
47 | |||
48 | using Nini.Config; | ||
49 | |||
50 | namespace 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 | } | ||