public BScene OptimizeScene(BScene bScene) { List <BInstance> newInstances = new List <BInstance>(); // Create collections of meshes with similar materials using (SceneAnalysis analysis = new SceneAnalysis(bScene)) { int lastInstanceCount = newInstances.Count; if (ConvOAR.Globals.parms.P <bool>("SeparateInstancedMeshes")) { newInstances.AddRange(SeparateMeshInstances(bScene, analysis)); } // Any shared meshes have been gathered into instances in 'newInstances' // and the meshes have been removed from the shared materials in the analysis. int instancesAdded = newInstances.Count - lastInstanceCount; ConvOAR.Globals.log.DebugFormat("{0} OptimizeScene: BInstances added by mesh instances = {1}", _logHeader, instancesAdded); lastInstanceCount = newInstances.Count; if (ConvOAR.Globals.parms.P <bool>("MergeSharedMaterialMeshes")) { newInstances.AddRange(MergeSharedMaterialMeshes(bScene, analysis)); } instancesAdded = newInstances.Count - lastInstanceCount; ConvOAR.Globals.log.DebugFormat("{0} OptimizeScene: BInstances added by material sharing = {1}", _logHeader, instancesAdded); } return(bScene); }
// Find all the meshes in passed Displayable and add them to the lists indexed by their material // mesh hashes. private void MapMaterialsAndMeshes(BScene pBs, BInstance pInst, Displayable disp) { RenderableMeshGroup rmg = disp.renderable as RenderableMeshGroup; if (rmg != null) { foreach (RenderableMesh rMesh in rmg.meshes) { InvertedMesh imesh = new InvertedMesh(pBs, pInst, disp, rmg, rMesh); BHash meshHash = rMesh.mesh.GetBHash(); if (!sharedMeshes.ContainsKey(meshHash)) { sharedMeshes.Add(meshHash, new List <InvertedMesh>()); } sharedMeshes[meshHash].Add(imesh); BHash materialHash = rMesh.material.GetBHash(); if (!meshByMaterial.ContainsKey(materialHash)) { meshByMaterial.Add(materialHash, new List <InvertedMesh>()); } meshByMaterial[materialHash].Add(imesh); } } foreach (Displayable child in disp.children) { MapMaterialsAndMeshes(pBs, pInst, child); } }
public void BuildAnalysis(BScene bScene) { foreach (BInstance inst in bScene.instances) { MapMaterialsAndMeshes(bScene, inst, inst.Representation); } }
public InvertedMesh(BScene pBs, BInstance pInst, Displayable pDisp, DisplayableRenderable pDisprend, RenderableMesh pRm) { containingScene = pBs; containingInstance = pInst; containingDisplayable = pDisp; containingDisplayableRenderable = pDisprend; renderableMesh = pRm; // Compute the global position of the Displayable globalPosition = containingDisplayable.offsetPosition * containingInstance.Rotation + containingInstance.Position; globalRotation = containingDisplayable.offsetRotation * containingInstance.Rotation; }
// Load the OARfile specified in Globals.params.InputOAR. // Parameters are in 'ConvOAR.Globals.params'. // For the moment, the OAR file must be specified with a string because of how OpenSimulator // processes the input files. Note that the filename can be an 'http:' type URL. public async Task <BScene> LoadOAR(IAssetService assetService, AssetManager assetManager) { BScene ret = null; try { OarConverter converter = new OarConverter(Globals.log, Globals.parms); ret = await converter.ConvertOarToScene(assetService, assetManager); } catch (Exception e) { ConvOAR.Globals.log.ErrorFormat("{0} LoadOAR exception: {1}", _logHeader, e); throw (e); } return(ret); }
// Return a new scene whos instances have been created by combining meshes that share // materials. public BScene RebuildSceneBasedOnSharedMeshes(BScene bScene) { List <BInstance> newInstances = new List <BInstance>(); // Create collections of meshes with similar materials using (SceneAnalysis analysis = new SceneAnalysis(bScene)) { newInstances.AddRange(MergeSharedMaterialMeshes(bScene, analysis)); } BScene newScene = new BScene(bScene); newScene.instances = newInstances; return(newScene); }
private List <BInstance> SeparateMeshInstances(BScene bScene, SceneAnalysis analysis) { List <BInstance> ret = new List <BInstance>(); try { // If there are lots of instances of the same mesh, it is better to have multiple instances // that point to the same mesh. If a mesh is not shared, consolidating the meshes // into a single instance is best. It's a balance of transferring vertices vs fewer draws. // Any meshes that are used more than 'MeshShareThreshold' will be sent out with their // instances rather than being combined. // The GLTF output code will not send out duplicate meshes and combining the meshes to // share materials destroys the duplicatable mesh shapes. // The duplicated meshes usually share a material so pull them together into meshes // in one instance. // Note: the 'SelectMany' is used to flatten the list of lists int meshShareThreshold = ConvOAR.Globals.parms.P <int>("MeshShareThreshold"); ConvOAR.Globals.log.DebugFormat("{0} SeparateMeshes: Separating instanced meshes. threshold={1}", _logHeader, meshShareThreshold); /* * foreach (BHash key in analysis.sharedMeshes.Keys) { // DEBUG DEBUG * ConvOAR.Globals.log.DebugFormat("{0} SeparateMeshes: mesh hash {1} . meshes={2}", // DEBUG DEBUG * _logHeader, key, analysis.sharedMeshes[key].Count); // DEBUG DEBUG * }; // DEBUG DEBUG */ ret.AddRange(analysis.sharedMeshes.Values.Where(val => val.Count > meshShareThreshold).SelectMany(meshList => { // Creates Instances for the shared messes in this list and also takes the meshes out of 'meshByMaterial' ConvOAR.Globals.log.DebugFormat("{0} MergeSharedMaterialMeshes: shared mesh hash: {1}/{2}, cnt={3}", _logHeader, meshList.First().renderableMesh.mesh.GetBHash(), meshList.First().renderableMesh.material.GetBHash(), meshList.Count); // Since mesh will be in this group, remove it from the meshes with shared materials analysis.MeshUsed(meshList); return(CreateInstancesForSharedMeshes(meshList)); }).ToList()); } catch (Exception e) { ConvOAR.Globals.log.DebugFormat("{0} SeparateMeshInstances: exception: {1}", _logHeader, e); } return(ret); }
private List <BInstance> MergeSharedMaterialMeshes(BScene bScene, SceneAnalysis analysis) { List <BInstance> ret = new List <BInstance>(); try { // 'analysis.meshByMaterial' has all meshes/instances grouped by material used // 'analysis.sharedMeshes' has all meshes grouped by the mesh ConvOAR.Globals.log.DebugFormat("{0} MergeShareMaterialHashes: number of materials = {1}", _logHeader, analysis.meshByMaterial.Count); // Merge the meshes and create an Instance containing the new mesh set ret.AddRange(analysis.meshByMaterial.Keys.SelectMany(materialHash => { ConvOAR.Globals.log.DebugFormat("{0} MergeShareMaterialHashes: material hash {1} . meshes={2}", _logHeader, materialHash, analysis.meshByMaterial[materialHash].Count); return(CreateInstancesFromSharedMaterialMeshes(materialHash, analysis.meshByMaterial[materialHash])); }).ToList()); } catch (Exception e) { ConvOAR.Globals.log.DebugFormat("{0} MergeShareMaterialHashes: exception: {1}", _logHeader, e); } return(ret); }
// If run from the command line, create instance and call 'Start' with args. // If run programmatically, create instance and call 'Start' with parameters. public async Task Start(CancellationToken cancelToken, string[] args) { Globals = new GlobalContext() { log = new LoggerLog4Net(), // log = new LoggerConsole(), stats = new ConvoarStats() }; Globals.parms = new ConvoarParams(Globals.log); // A single parameter of '--help' outputs the invocation parameters if (args.Length > 0 && args[0] == "--help") { System.Console.Write(Invocation()); return; } // 'ConvoarParams' initializes to default values. // Over ride default values with command line parameters. try { // Note that trailing parameters will be put into "InputOAR" parameter Globals.parms.MergeCommandLine(args, null, "InputOAR"); } catch (Exception e) { Globals.log.ErrorFormat("ERROR: bad parameters: " + e.Message); Globals.log.ErrorFormat(Invocation()); return; } if (Globals.parms.P <bool>("Verbose")) { Globals.log.SetVerbose(Globals.parms.P <bool>("Verbose")); } if (!Globals.parms.P <bool>("Quiet")) { System.Console.WriteLine("Convoar v" + Globals.version + " built " + Globals.buildDate + " commit " + Globals.gitCommit ); } // Validate parameters if (String.IsNullOrEmpty(Globals.parms.P <string>("InputOAR"))) { Globals.log.ErrorFormat("An input OAR file must be specified"); Globals.log.ErrorFormat(Invocation()); return; } if (String.IsNullOrEmpty(Globals.parms.P <string>("OutputDir"))) { _outputDir = "./out"; Globals.log.DebugFormat("Output directory defaulting to {0}", _outputDir); } // Base asset storage system -- 'MemAssetService' is in-memory storage using (MemAssetService memAssetService = new MemAssetService()) { // 'assetManager' is the asset cache and fetching code -- where all the mesh, // material, and instance information is stored for later processing. using (AssetManager assetManager = new AssetManager(memAssetService, Globals.log, Globals.parms)) { try { BScene bScene = await LoadOAR(memAssetService, assetManager); Globals.contextName = bScene.name; Globals.log.DebugFormat("{0} Scene created. name={1}, instances={2}", _logHeader, bScene.name, bScene.instances.Count); Globals.log.DebugFormat("{0} num assetFetcher.images={1}", _logHeader, assetManager.Assets.Images.Count); Globals.log.DebugFormat("{0} num assetFetcher.materials={1}", _logHeader, assetManager.Assets.Materials.Count); Globals.log.DebugFormat("{0} num assetFetcher.meshes={1}", _logHeader, assetManager.Assets.Meshes.Count); Globals.log.DebugFormat("{0} num assetFetcher.renderables={1}", _logHeader, assetManager.Assets.Renderables.Count); if (ConvOAR.Globals.parms.P <bool>("AddTerrainMesh")) { ConvOAR.Globals.log.DebugFormat("{0} Adding terrain to scene", _logHeader); bScene.instances.Add(bScene.terrainInstance); } if (ConvOAR.Globals.parms.P <bool>("TerrainOnly")) { ConvOAR.Globals.log.DebugFormat("{0} Clearing out scene so there's only terrain (TerrainOnly)", _logHeader); bScene.instances.Clear(); bScene.instances.Add(bScene.terrainInstance); } /* * // Perform any optimizations on the scene and its instances * if (Globals.parms.P<bool>("DoMeshSimplification")) { * // TODO: * } * if (Globals.parms.P<bool>("DoSceneOptimizations")) { * using (BSceneManipulation optimizer = new BSceneManipulation()) { * bScene = optimizer.OptimizeScene(bScene); * Globals.log.DebugFormat("{0} merged BScene. numInstances={1}", _logHeader, bScene.instances.Count); * } * } */ if (Globals.parms.P <bool>("MergeSharedMaterialMeshes")) { using (BSceneManipulation optimizer = new BSceneManipulation(Globals.log, Globals.parms)) { bScene = optimizer.RebuildSceneBasedOnSharedMeshes(bScene); Globals.log.DebugFormat("{0} merged meshes in scene. numInstances={1}", _logHeader, bScene.instances.Count); } } // Output the transformed scene as Gltf version 2 Gltf gltf = new Gltf(bScene.name, Globals.log, Globals.parms); try { gltf.LoadScene(bScene); Globals.log.DebugFormat("{0} num Gltf.nodes={1}", _logHeader, gltf.nodes.Count); Globals.log.DebugFormat("{0} num Gltf.meshes={1}", _logHeader, gltf.meshes.Count); Globals.log.DebugFormat("{0} num Gltf.materials={1}", _logHeader, gltf.materials.Count); Globals.log.DebugFormat("{0} num Gltf.images={1}", _logHeader, gltf.images.Count); Globals.log.DebugFormat("{0} num Gltf.accessor={1}", _logHeader, gltf.accessors.Count); Globals.log.DebugFormat("{0} num Gltf.buffers={1}", _logHeader, gltf.buffers.Count); Globals.log.DebugFormat("{0} num Gltf.bufferViews={1}", _logHeader, gltf.bufferViews.Count); } catch (Exception e) { Globals.log.ErrorFormat("{0} Exception loading GltfScene: {1}", _logHeader, e); } try { if (gltf.scenes.Count > 0) { string gltfFilename = gltf.GetFilename(gltf.IdentifyingString); using (var outm = new MemoryStream()) { using (var outt = new StreamWriter(outm)) { gltf.ToJSON(outt); } await assetManager.AssetStorage.Store(gltfFilename, outm.ToArray()); } gltf.WriteBinaryFiles(assetManager.AssetStorage); if (Globals.parms.P <bool>("ExportTextures")) { gltf.WriteImages(assetManager.AssetStorage); } } else { Globals.log.ErrorFormat("{0} Not writing out GLTF because no scenes", _logHeader); } } catch (Exception e) { Globals.log.ErrorFormat("{0} Exception writing GltfScene: {1}", _logHeader, e); } /* * // Output all the instances in the scene as individual GLTF files * if (Globals.parms.P<bool>("ExportIndividualGltf")) { * bScene.instances.ForEach(instance => { * string instanceName = instance.handle.ToString(); * Gltf gltf = new Gltf(instanceName); * gltf.persist.baseDirectory = bScene.name; * // gltf.persist.baseDirectory = PersistRules.JoinFilePieces(bScene.name, instanceName); * GltfScene gltfScene = new GltfScene(gltf, instanceName); * gltf.defaultScene = gltfScene; * * Displayable rootDisp = instance.Representation; * GltfNode rootNode = GltfNode.GltfNodeFactory(gltf, gltfScene, rootDisp, assetFetcher); * rootNode.translation = instance.Position; * rootNode.rotation = instance.Rotation; * * gltf.BuildAccessorsAndBuffers(); * gltf.UpdateGltfv2ReferenceIndexes(); * * // After the building, get rid of the default scene name as we're not outputting a scene * gltf.defaultScene = null; * * PersistRules.ResolveAndCreateDir(gltf.persist.filename); * * using (StreamWriter outt = File.CreateText(gltf.persist.filename)) { * gltf.ToJSON(outt); * } * gltf.WriteBinaryFiles(); * * if (Globals.parms.P<bool>("ExportTextures")) { * gltf.WriteImages(); * } * }); * } */ } catch (Exception e) { Globals.log.ErrorFormat("{0} Global exception converting scene: {1}", _logHeader, e); // A common error is not having all the DLLs for OpenSimulator. Print out what's missing. if (e is ReflectionTypeLoadException refE) { foreach (var ee in refE.LoaderExceptions) { Globals.log.ErrorFormat("{0} reference exception: {1}", _logHeader, ee); } } } } } }
// Create a new scene based on an existing scene. // NOTE: this is NOT a clone. Instances are not copied and other things just // have their pointers moved so the items are shared. public BScene(BScene bScene) { name = bScene.name; attributes = bScene.attributes; terrainInstance = bScene.terrainInstance; }
public Promise <BScene> ConvertOarToScene(IAssetService assetService, IAssetFetcher assetFetcher) { Promise <BScene> prom = new Promise <BScene>(); // Assemble all the parameters that loadoar takes and uses Dictionary <string, object> options = new Dictionary <string, object>(); // options.Add("merge", false); options.Add("displacement", ConvOAR.Globals.parms.P <OMV.Vector3>("Displacement")); string optRotation = ConvOAR.Globals.parms.P <string>("Rotation"); if (optRotation != null) { options.Add("rotation", float.Parse(optRotation, System.Threading.Thread.CurrentThread.CurrentCulture)); } // options.Add("default-user", OMV.UUID.Random()); // if (optSkipAssets != null) options.Add('skipAssets', true); // if (optForceTerrain != null) options.Add("force-terrain", true); // if (optNoObjects != null) options.Add("no-objects", true); string optSubRegion = ConvOAR.Globals.parms.P <string>("SubRegion"); if (optSubRegion != null) { List <float> bounds = optSubRegion.Split(',').Select <string, float>(x => { return(float.Parse(x)); }).ToList(); options.Add("bounding-origin", new OMV.Vector3(bounds[0], bounds[1], bounds[2])); options.Add("bounding-size", new OMV.Vector3(bounds[3] - bounds[0], bounds[4] - bounds[1], bounds[5] - bounds[2])); } // Create an OpenSimulator region and scene to load the OAR into string regionName = "convoar"; if (String.IsNullOrEmpty(ConvOAR.Globals.parms.P <String>("RegionName"))) { // Try to build the region name from the OAR filesname regionName = Path.GetFileNameWithoutExtension(ConvOAR.Globals.parms.P <string>("InputOAR")); } else { regionName = ConvOAR.Globals.parms.P <string>("RegionName"); } Scene scene = CreateScene(assetService, regionName); // Load the archive into our scene ArchiveReadRequest archive = new ArchiveReadRequest(scene, ConvOAR.Globals.parms.P <string>("InputOAR"), Guid.Empty, options); archive.DearchiveRegion(false); // Convert SOGs from OAR into EntityGroups // ConvOAR.Globals.log.Log("Num assets = {0}", assetService.NumAssets); LogBProgress("Num SOGs = {0}", scene.GetSceneObjectGroups().Count); PrimToMesh mesher = new PrimToMesh(); // Convert SOGs => BInstances Promise <BInstance> .All( scene.GetSceneObjectGroups().Select(sog => { return(ConvertSogToInstance(sog, assetFetcher, mesher)); }) ) .Done(instances => { ConvOAR.Globals.log.DebugFormat("{0} Num instances = {1}", _logHeader, instances.ToList().Count); List <BInstance> instanceList = new List <BInstance>(); instanceList.AddRange(instances); // Add the terrain mesh to the scene BInstance terrainInstance = null; if (ConvOAR.Globals.parms.P <bool>("AddTerrainMesh")) { ConvOAR.Globals.log.DebugFormat("{0} Creating terrain for scene", _logHeader); // instanceList.Add(ConvoarTerrain.CreateTerrainMesh(scene, mesher, assetFetcher)); terrainInstance = ConvoarTerrain.CreateTerrainMesh(scene, mesher, assetFetcher); CoordAxis.FixCoordinates(terrainInstance, new CoordAxis(CoordAxis.RightHand_Yup | CoordAxis.UVOriginLowerLeft)); } // Twist the OpenSimulator Z-up coordinate system to the OpenGL Y-up foreach (var inst in instanceList) { CoordAxis.FixCoordinates(inst, new CoordAxis(CoordAxis.RightHand_Yup | CoordAxis.UVOriginLowerLeft)); } // package instances into a BScene BScene bScene = new BScene(); bScene.instances = instanceList; RegionInfo ri = scene.RegionInfo; bScene.name = ri.RegionName; bScene.terrainInstance = terrainInstance; bScene.attributes.Add("RegionName", ri.RegionName); bScene.attributes.Add("RegionSizeX", ri.RegionSizeX); bScene.attributes.Add("RegionSizeY", ri.RegionSizeY); bScene.attributes.Add("RegionSizeZ", ri.RegionSizeZ); bScene.attributes.Add("RegionLocX", ri.RegionLocX); bScene.attributes.Add("RegionLocY", ri.RegionLocY); bScene.attributes.Add("WorldLocX", ri.WorldLocX); bScene.attributes.Add("WorldLocY", ri.WorldLocY); bScene.attributes.Add("WaterHeight", ri.RegionSettings.WaterHeight); bScene.attributes.Add("DefaultLandingPorint", ri.DefaultLandingPoint); prom.Resolve(bScene); }, e => { ConvOAR.Globals.log.ErrorFormat("{0} failed SOG conversion: {1}", _logHeader, e); // prom.Reject(new Exception(String.Format("Failed conversion: {0}", e))); }); return(prom); }
public SceneAnalysis(BScene bScene) { this.scene = bScene; BuildAnalysis(bScene); }