void loadGeometryFile(string filename) { var sr = new StreamReader(Application.dataPath + "/" + filename); var fileContents = sr.ReadToEnd(); sr.Close(); string[] lines = fileContents.Split("\n"[0]); foreach (string line in lines) { string[] v = line.Split(' '); if (v.Length >= 3) { float height = 0.0f; int indexCorrection = 0; if (!float.TryParse(v[v.Length - 1], out height)) { indexCorrection = 1; } if ((v.Length + indexCorrection) % 2 == 0) { string name = v[v.Length - 3 + indexCorrection]; List <Vector2> list = new List <Vector2>(); for (int i = 1; i < v.Length - 4 + indexCorrection; i = i + 2) { float x; float y; if (float.TryParse(v[i], out x) && float.TryParse(v[i + 1], out y)) { list.Add(new Vector2(x, y)); } } string type = v[v.Length - 2 + indexCorrection]; if (type == "wall") { WallExtrudeGeometry.create(name, list, height, -0.2f); } else if (type == "obstacle") { ObstacleExtrudeGeometry.create(name, list, height); } else if (type == "tree") { TreeGeometry.create(name, list[0]); } else { AreaGeometry.create(name, list); } } } } }
internal void AddCircleVisualization(Transform parent, float radius) { var scale = TreeGeometry.SizeToScale(radius, DefaultCirclePlaneRadius, 1); var cirvleObject = InstantiateObject(CircleName, CirclePlane, parent, localScale: new Vector3(scale, 1, scale)); cirvleObject.AddComponent <CircleInputHandler>(); var disposable = ApplicationManager.Instance.AppState.Settings.VisualizeCircles.Subscribe(visualize => { cirvleObject.SetActive(visualize); }); cirvleObject.OnDestroyAsObservable().Subscribe(_ => disposable.Dispose()); }
/// <summary> /// This method must be called whenever the camera changes. It gets the new /// camera dependent rendering arguments from SpeedTree. /// </summary> protected void UpdateRenderArgs() { // let speedtree compute the LOD for this tree speedTree.ComputeLodLevel(); // update geometry for all parts TreeGeometry geometry = group.Geometry; speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.AllGeometry, -1, -1, -1); // copy out branch args branchRenderArgs.AlphaTestValue = (int)geometry.BranchAlphaTestValue; branchRenderArgs.LOD = geometry.Branches.DiscreteLodLevel; branchRenderArgs.Active = branchRenderArgs.AlphaTestValue < 255; // copy out frond args frondRenderArgs.AlphaTestValue = (int)geometry.FrondAlphaTestValue; frondRenderArgs.LOD = geometry.Fronds.DiscreteLodLevel; frondRenderArgs.Active = frondRenderArgs.AlphaTestValue < 255; // copy out leaf args leaf0RenderArgs.AlphaTestValue = (int)geometry.Leaves0.AlphaTestValue; leaf0RenderArgs.LOD = geometry.Leaves0.DiscreteLodLevel; leaf0RenderArgs.Active = geometry.Leaves0.IsActive; leaf1RenderArgs.AlphaTestValue = (int)geometry.Leaves1.AlphaTestValue; leaf1RenderArgs.LOD = geometry.Leaves1.DiscreteLodLevel; leaf1RenderArgs.Active = geometry.Leaves1.IsActive; // copy out billboard args // NOTE - billboards dont have LOD billboard0RenderArgs.AlphaTestValue = (int)geometry.Billboard0.AlphaTestValue; billboard0RenderArgs.Active = geometry.Billboard0.IsActive; billboard0RenderArgs.LOD = 0; billboard1RenderArgs.AlphaTestValue = (int)geometry.Billboard1.AlphaTestValue; billboard1RenderArgs.Active = geometry.Billboard1.IsActive; billboard1RenderArgs.LOD = 0; if (billboard0RenderArgs.Active) { FillBillboardBuffer(billboard0, geometry.Billboard0); } if (billboard1RenderArgs.Active) { FillBillboardBuffer(billboard1, geometry.Billboard1); } group.AddVisible(this, branchRenderArgs.Active, frondRenderArgs.Active, leaf0RenderArgs.Active || leaf1RenderArgs.Active, billboard0RenderArgs.Active || billboard1RenderArgs.Active); }
/// <summary> /// Sets position of node according to the circle property and adjust edge properly /// </summary> /// <param name="circle"></param> /// <param name="node"></param> /// <param name="edge"></param> /// <param name="centralLength"></param> internal void SubscribeNodePosition(Circle circle, GameObject node, GameObject edge, float centralLength) { circle.Position.Subscribe(v => { var y = node.transform.localPosition.y; node.transform.localPosition = new Vector3(v.x, y, v.y); var phi = TreeGeometry.CalcAlpha(v.x, v.y); var theta = TreeGeometry.CalcTheta(centralLength, v.magnitude); edge.transform.localEulerAngles = new Vector3(theta, phi, 0); var diameter = edge.transform.localScale.x; var l = TreeGeometry.CalcEdgeLength(centralLength, theta); edge.transform.localScale = new Vector3(diameter, TreeGeometry.SizeToScale(l, DefaultEdgeHeight, DefaultEdgeScale.y), diameter); }); }
public void FillBillboardBuffer(float [] buffer, TreeGeometry.Billboard billboard) { unsafe { float * srcCoord = billboard.Coords; float * srcTexCoord = billboard.TexCoords; // Position buffer[0] = srcCoord[0] + location.x; buffer[1] = srcCoord[1] + location.y; buffer[2] = srcCoord[2] + location.z; // Texture buffer[3] = srcTexCoord[0]; buffer[4] = srcTexCoord[1]; // Position buffer[5] = srcCoord[3] + location.x; buffer[6] = srcCoord[4] + location.y; buffer[7] = srcCoord[5] + location.z; // Texture buffer[8] = srcTexCoord[2]; buffer[9] = srcTexCoord[3]; // Position buffer[10] = srcCoord[6] + location.x; buffer[11] = srcCoord[7] + location.y; buffer[12] = srcCoord[8] + location.z; // Texture buffer[13] = srcTexCoord[4]; buffer[14] = srcTexCoord[5]; // Position buffer[15] = srcCoord[9] + location.x; buffer[16] = srcCoord[10] + location.y; buffer[17] = srcCoord[11] + location.z; // Texture buffer[18] = srcTexCoord[6]; buffer[19] = srcTexCoord[7]; } return; }
public void BuildLeafBuffer(TreeGeometry.Leaf leaf, float leafAdjust, out VertexData vertexData, out IndexData indexData) { if (leaf.LeafCount == 0) { vertexData = null; indexData = null; } else { // // Build the vertex buffer // vertexData = new VertexData(); vertexData.vertexCount = leaf.LeafCount * 4; vertexData.vertexStart = 0; // destroy the original vertex declaration, and use the common one for leaves HardwareBufferManager.Instance.DestroyVertexDeclaration(vertexData.vertexDeclaration); vertexData.vertexDeclaration = leafVertexDeclaration; // create the hardware vertex buffer and set up the buffer binding HardwareVertexBuffer hvBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( vertexData.vertexDeclaration.GetVertexSize(0), vertexData.vertexCount, BufferUsage.StaticWriteOnly, false); vertexData.vertexBufferBinding.SetBinding(0, hvBuffer); // lock the vertex buffer IntPtr ipBuf = hvBuffer.Lock(BufferLocking.Discard); int bufferOff = 0; int maxindex = 0; unsafe { float* buffer = (float*)ipBuf.ToPointer(); float* srcCenter = leaf.CenterCoords; float* srcNormal = leaf.Normals; byte* srcWindIndices = leaf.WindMatrixIndices; float* srcWindWeights = leaf.WindWeights; byte* srcLeafClusterIndices = leaf.LeafClusterIndices; for (int l = 0; l < leaf.LeafCount; l++) { float* srcOffset = leaf.LeafMapCoords[l]; float* srcTexCoord = leaf.LeafMapTexCoords[l]; int leafClusterIndex = *srcLeafClusterIndices; //if (leafClusterIndex % 4 == 0) //{ // leafClusterIndex++; //} if (leafClusterIndex > maxindex) { maxindex = leafClusterIndex; } // Position buffer[bufferOff++] = srcCenter[0]; buffer[bufferOff++] = srcCenter[1]; buffer[bufferOff++] = srcCenter[2]; // normals buffer[bufferOff++] = srcNormal[0]; buffer[bufferOff++] = srcNormal[1]; buffer[bufferOff++] = srcNormal[2]; // Texture buffer[bufferOff++] = srcTexCoord[0]; buffer[bufferOff++] = srcTexCoord[1]; // Wind Attributes buffer[bufferOff++] = *srcWindIndices; buffer[bufferOff++] = *srcWindWeights; // Leaf Placement Data buffer[bufferOff++] = leafClusterIndex * 4; buffer[bufferOff++] = leafAdjust; buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices); // Position buffer[bufferOff++] = srcCenter[0]; buffer[bufferOff++] = srcCenter[1]; buffer[bufferOff++] = srcCenter[2]; // normals buffer[bufferOff++] = srcNormal[0]; buffer[bufferOff++] = srcNormal[1]; buffer[bufferOff++] = srcNormal[2]; // Texture buffer[bufferOff++] = srcTexCoord[2]; buffer[bufferOff++] = srcTexCoord[3]; // Wind Attributes buffer[bufferOff++] = *srcWindIndices; buffer[bufferOff++] = *srcWindWeights; // Leaf Placement Data buffer[bufferOff++] = leafClusterIndex * 4 + 1; buffer[bufferOff++] = leafAdjust; buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices); // Position buffer[bufferOff++] = srcCenter[0]; buffer[bufferOff++] = srcCenter[1]; buffer[bufferOff++] = srcCenter[2]; // normals buffer[bufferOff++] = srcNormal[0]; buffer[bufferOff++] = srcNormal[1]; buffer[bufferOff++] = srcNormal[2]; // Texture buffer[bufferOff++] = srcTexCoord[4]; buffer[bufferOff++] = srcTexCoord[5]; // Wind Attributes buffer[bufferOff++] = *srcWindIndices; buffer[bufferOff++] = *srcWindWeights; // Leaf Placement Data buffer[bufferOff++] = leafClusterIndex * 4 + 2; buffer[bufferOff++] = leafAdjust; buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices); // Position buffer[bufferOff++] = srcCenter[0]; buffer[bufferOff++] = srcCenter[1]; buffer[bufferOff++] = srcCenter[2]; // normals buffer[bufferOff++] = srcNormal[0]; buffer[bufferOff++] = srcNormal[1]; buffer[bufferOff++] = srcNormal[2]; // Texture buffer[bufferOff++] = srcTexCoord[6]; buffer[bufferOff++] = srcTexCoord[7]; // Wind Attributes buffer[bufferOff++] = *srcWindIndices++; buffer[bufferOff++] = *srcWindWeights++; // Leaf Placement Data buffer[bufferOff++] = leafClusterIndex * 4 + 3; buffer[bufferOff++] = leafAdjust; buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices); srcCenter += 3; srcNormal += 3; srcLeafClusterIndices++; } } hvBuffer.Unlock(); // Turned off this output because it kills performance when editing terrain // Console.WriteLine("max leaf cluster index: {0}", maxindex); // // build the index buffer // indexData = new IndexData(); int numIndices; numIndices = leaf.LeafCount * 6; indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer( IndexType.Size16, numIndices, BufferUsage.StaticWriteOnly); IntPtr indexBufferPtr = indexData.indexBuffer.Lock(0, indexData.indexBuffer.Size, BufferLocking.Discard); unsafe { ushort* indexBuffer = (ushort*)indexBufferPtr.ToPointer(); int bufOff = 0; int vert = 0; for (int i = 0; i < leaf.LeafCount; i++) { indexBuffer[bufOff++] = (ushort)vert; indexBuffer[bufOff++] = (ushort)(vert + 1); indexBuffer[bufOff++] = (ushort)(vert + 2); indexBuffer[bufOff++] = (ushort)vert; indexBuffer[bufOff++] = (ushort)(vert + 2); indexBuffer[bufOff++] = (ushort)(vert + 3); vert += 4; } } indexData.indexBuffer.Unlock(); indexData.indexCount = numIndices; indexData.indexStart = 0; } return; }
public void BuildIndexedBuffer(TreeGeometry.Indexed indexed, bool normalMapped, out VertexData vertexData, out IndexData indexData) { // // Build the vertex buffer // if (indexed.VertexCount == 0) { vertexData = null; indexData = null; } else { vertexData = new VertexData(); vertexData.vertexCount = indexed.VertexCount; vertexData.vertexStart = 0; // free the vertex declaration to avoid a leak. HardwareBufferManager.Instance.DestroyVertexDeclaration(vertexData.vertexDeclaration); // pick from the common vertex declarations based on whether we are normal mapped or not if (normalMapped) { vertexData.vertexDeclaration = indexedNormalMapVertexDeclaration; } else { vertexData.vertexDeclaration = indexedVertexDeclaration; } // create the hardware vertex buffer and set up the buffer binding HardwareVertexBuffer hvBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( vertexData.vertexDeclaration.GetVertexSize(0), vertexData.vertexCount, BufferUsage.StaticWriteOnly, false); vertexData.vertexBufferBinding.SetBinding(0, hvBuffer); // lock the vertex buffer IntPtr ipBuf = hvBuffer.Lock(BufferLocking.Discard); int bufferOff = 0; unsafe { float* buffer = (float*)ipBuf.ToPointer(); float* srcPosition = indexed.Coords; float* srcNormal = indexed.Normals; float* srcTexCoord = indexed.TexCoords0; float* srcShadowTexCoord = indexed.TexCoords1; byte* srcWindIndices = indexed.WindMatrixIndices; float* srcWindWeights = indexed.WindWeights; float* srcBinormal = indexed.Binormals; float* srcTangent = indexed.Tangents; for (int v = 0; v < vertexData.vertexCount; v++) { // Position buffer[bufferOff++] = srcPosition[0]; buffer[bufferOff++] = srcPosition[1]; buffer[bufferOff++] = srcPosition[2]; srcPosition += 3; // normals buffer[bufferOff++] = *srcNormal++; buffer[bufferOff++] = *srcNormal++; buffer[bufferOff++] = *srcNormal++; // Texture buffer[bufferOff++] = *srcTexCoord++; buffer[bufferOff++] = *srcTexCoord++; // Self Shadow Texture buffer[bufferOff++] = *srcShadowTexCoord++; buffer[bufferOff++] = *srcShadowTexCoord++; // wind params buffer[bufferOff++] = *srcWindIndices++; buffer[bufferOff++] = *srcWindWeights++; if (normalMapped) { // Binormal buffer[bufferOff++] = *srcBinormal++; buffer[bufferOff++] = *srcBinormal++; buffer[bufferOff++] = *srcBinormal++; // Tangent buffer[bufferOff++] = *srcTangent++; buffer[bufferOff++] = *srcTangent++; buffer[bufferOff++] = *srcTangent++; } } } hvBuffer.Unlock(); // // build the index buffer // if (indexed.NumStrips == 0) { indexData = null; } else { indexData = new IndexData(); int numIndices; unsafe { numIndices = indexed.StripLengths[0]; } indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer( IndexType.Size16, numIndices, BufferUsage.StaticWriteOnly); IntPtr indexBufferPtr = indexData.indexBuffer.Lock(0, indexData.indexBuffer.Size, BufferLocking.Discard); unsafe { ushort* strip = null; if (numIndices != 0) { strip = indexed.Strips[0]; } ushort* indexBuffer = (ushort*)indexBufferPtr.ToPointer(); for (int i = 0; i < numIndices; i++) { indexBuffer[i] = strip[i]; } } indexData.indexBuffer.Unlock(); indexData.indexCount = numIndices; indexData.indexStart = 0; } return; } }
public TreeGroup(String filename, float size, float sizeVariance, SpeedWindWrapper speedWind, Forest forest, List<Vector3> locations) { if (!initialized) { Initialize(); } name = String.Format("Forest: {0} File: {1} Instances: {2}", forest.Name, filename, locations.Count); this.forest = forest; this.speedWind = speedWind; speedTree = new SpeedTreeWrapper(); speedTree.TextureFlip = true; LoadTree(filename); speedTree.BranchWindMethod = WindMethod.WindGPU; speedTree.FrondWindMethod = WindMethod.WindGPU; speedTree.LeafWindMethod = WindMethod.WindGPU; float originalSize = 0f; float variance = 0f; speedTree.GetTreeSize(ref originalSize, ref variance); speedTree.OriginalSize = originalSize; speedTree.SetTreeSize(size, sizeVariance); treeTextures = speedTree.Textures; // make sure the tree doesn't have too many leaf texture groups Debug.Assert(treeTextures.LeafTextureFilenames.Length <= 3); // for trees with 3 leaf textures, reduce the number of rocking groups to 2 // (from the default of 3), so that we don't overflow the number of available shader // param registers if (treeTextures.LeafTextureFilenames.Length == 3) { speedTree.NumLeafRockingGroups = 2; } speedTree.Compute(SpeedTreeUtil.ToSpeedTree(Matrix4.Identity), 1, true); speedTree.TreePosition = SpeedTreeUtil.ToSpeedTree(Vector3.Zero); // set lod limits speedTree.SetLodLimits(nearLOD, farLOD); // create the geometry object geometry = new TreeGeometry(); // // Setup branches // // create the render operation branchRenderOp = new RenderOperation(); branchRenderOp.operationType = OperationType.TriangleStrip; branchRenderOp.useIndices = true; // set up the material. branchMaterial = SetupTreeMaterial("SpeedTree/Branch", treeTextures.BranchTextureFilename, treeTextures.SelfShadowFilename, GenerateNormalMapTextureName(treeTextures.BranchTextureFilename), speedTree.BranchMaterial, !normalMapped, branchTechnique); // get number of branch LODs uint nBranchLODs = speedTree.NumBranchLodLevels; // allocate branch buffers branchVertexBuffers = new VertexData[nBranchLODs]; branchIndexBuffers = new IndexData[nBranchLODs]; for (short i = 0; i < nBranchLODs; i++) { // set up the vertex and index buffers for the branch geometry speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.BranchGeometry, i, -1, -1); BuildIndexedBuffer(geometry.Branches, true, out branchVertexBuffers[i], out branchIndexBuffers[i]); } // // Setup fronds // // create the render operation frondRenderOp = new RenderOperation(); frondRenderOp.operationType = OperationType.TriangleStrip; frondRenderOp.useIndices = true; // set up the material frondMaterial = SetupTreeMaterial("SpeedTree/Frond", treeTextures.CompositeFilename, treeTextures.SelfShadowFilename, null, speedTree.FrondMaterial, true, 0); uint nFrondLODs = speedTree.NumFrondLodLevels; // allocate frond buffer arrays frondVertexBuffers = new VertexData[nFrondLODs]; frondIndexBuffers = new IndexData[nFrondLODs]; for ( short i = 0; i < nFrondLODs; i++ ) { // build the frond geometry for each LOD speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.FrondGeometry, -1, i, -1); BuildIndexedBuffer(geometry.Fronds, false, out frondVertexBuffers[i], out frondIndexBuffers[i]); } // // Setup Leaves // TreeCamera saveCam = SpeedTreeWrapper.Camera; TreeCamera treeCamera = new TreeCamera(); treeCamera.position = SpeedTreeUtil.ToSpeedTree(Vector3.Zero); treeCamera.direction = SpeedTreeUtil.ToSpeedTree(new Vector3(1,0,0)); SpeedTreeWrapper.Camera = treeCamera; // set up render ops leaf0RenderOp = new RenderOperation(); leaf0RenderOp.operationType = OperationType.TriangleList; leaf0RenderOp.useIndices = true; leaf1RenderOp = new RenderOperation(); leaf1RenderOp.operationType = OperationType.TriangleList; leaf1RenderOp.useIndices = true; // set up the material leafMaterial = SetupTreeMaterial("SpeedTree/Leaf", treeTextures.CompositeFilename, null, null, speedTree.LeafMaterial, true, 0); uint nLeafLODs = speedTree.NumLeafLodLevels; // allocate leaf buffer arrays leafVertexBuffers = new VertexData[nLeafLODs]; leafIndexBuffers = new IndexData[nLeafLODs]; float [] lodLeafAdjust = speedTree.LeafLodSizeAdjustments; for ( short i = 0; i < nLeafLODs; i++ ) { // build the leaf geometry for each LOD speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.LeafGeometry, -1, -1, i); BuildLeafBuffer(geometry.Leaves0, lodLeafAdjust[i], out leafVertexBuffers[i], out leafIndexBuffers[i]); } // restore the camera afte getting leaf buffers SpeedTreeWrapper.Camera = saveCam; bounds = new AxisAlignedBox(); // build all the trees and accumulate bounds foreach (Vector3 loc in locations) { SpeedTreeWrapper treeInstance = speedTree.MakeInstance(); treeInstance.OriginalSize = originalSize; Tree t = new Tree(this, treeInstance, loc); bounds.Merge(t.Bounds); trees.Add(t); } boundingRadius = (bounds.Maximum - bounds.Minimum).Length / 2; // create the buckets branchBuckets = new Dictionary<int, List<Tree>>[nBranchLODs]; frondBuckets = new Dictionary<int, List<Tree>>[nFrondLODs]; leafBuckets = new Dictionary<int, List<Tree>>[nLeafLODs]; billboardBucket = new Dictionary<int, List<Tree>>[1]; // initialize the bucket dictionaries for (int i = 0; i < nBranchLODs; i++) { branchBuckets[i] = new Dictionary<int, List<Tree>>(); } for (int i = 0; i < nFrondLODs; i++) { frondBuckets[i] = new Dictionary<int, List<Tree>>(); } for (int i = 0; i < nLeafLODs; i++) { leafBuckets[i] = new Dictionary<int, List<Tree>>(); } billboardBucket[0] = new Dictionary<int, List<Tree>>(); allGroups.Add(this); }