private void SampleFunction(GridSample grid) { float z = 0; int i = 0; for (; i < numberOfBalls; ++i) { Metaball ball = _metaballs[i]; float x2 = grid.x - ball.position.x; float y2 = grid.y - ball.position.y; z += (ball.radius * ball.radius) / ((x2 * x2) + (y2 * y2)); } grid.sample = z; }
private Mesh CreateMesh(Metaball metaball, Device device) { var mesh = new Mesh( device, NumIndices / 3, NumVertices, MeshFlags.Dynamic | MeshFlags.WriteOnly, VertexFormat.PositionNormal ); // lock buffers sVx = mesh.LockVertexBuffer(LockFlags.Discard); sIx = mesh.LockIndexBuffer(LockFlags.Discard); try { // write buffers unsafe { fixed(sVxBuffer *FixTemp = &VxBuffer[0]) { IntPtr VxPointer = new IntPtr(FixTemp); sVx.WriteRange(VxPointer, sizeof(sVxBuffer) * NumVertices); } fixed(short *FixTemp = &IxBuffer[0]) { IntPtr IxPointer = new IntPtr(FixTemp); sIx.WriteRange(IxPointer, sizeof(short) * NumIndices); } } } finally { // unlock buffers mesh.UnlockIndexBuffer(); mesh.UnlockVertexBuffer(); } return(mesh); }
private void GenerateMetaballs() { metaballs = new Metaball[metaballCount]; for (int i = 0; i < metaballCount; i++) { Metaball m = new Metaball(); m.position = new Vector3(); // m.position.x = Random.Range(0, size); // m.position.y = Random.Range(0, size); m.position.x = size * 0.5f; m.position.y = size * 0.5f; m.radius = Random.Range(Mathf.Min(minSize, maxSize), Mathf.Max(minSize, maxSize)); m.color = Random.ColorHSV(0f, 1f, 0.8f, 1f, 0.1f, 1f); m.velocity = Random.onUnitSphere * maxVelocity * 2f; m.velocity.z = 0; ClampVelocity(m); metaballs[i] = m; } }
public void evaluateAll() { if (metaballs == null) { return; } if (!initialized) { init(); } // write info about metaballs in format readable by compute shaders gpuBalls = new GPUBall[metaballs.Length]; for (int i = 0; i < metaballs.Length; i++) { Metaball metaball = metaballs[i]; gpuBalls[i].position = metaballRenderer.transform.localToWorldMatrix.inverse.MultiplyPoint3x4(metaball.transform.position); gpuBalls[i].factor = metaball.factor / Mathf.Pow(((metaballRenderer.transform.lossyScale.x + metaballRenderer.transform.lossyScale.y + metaballRenderer.transform.lossyScale.z) / 3.0f), 2.0f); } runComputeShader(gpuBalls); }
private void ClampVelocity2(Metaball m) { if (m.position.x + m.radius >= bounds.max.x) { m.velocity = -m.velocity; m.position.x = bounds.max.x - m.radius; } else if (m.position.x - m.radius <= bounds.min.x) { m.velocity = -m.velocity; m.position.x = bounds.min.x + m.radius; } if (m.position.y + m.radius >= bounds.max.y) { m.velocity = -m.velocity; m.position.y = bounds.max.y - m.radius; } else if (m.position.y - m.radius <= bounds.min.y) { m.velocity = -m.velocity; m.position.y = bounds.min.y + m.radius; } }
protected void Update() { // update ball positions int i = 0; for (; i < numberOfBalls; ++i) { Metaball ball = _metaballs[i]; ball.position += ball.velocity * Time.deltaTime; if (ball.position.x > bounds.xMax || ball.position.x < bounds.xMin) { ball.velocity.x = -ball.velocity.x; } if (ball.position.y > bounds.yMax || ball.position.y < bounds.yMin) { ball.velocity.y = -ball.velocity.y; } } // update grid samples for new metaball positions int l = _grid.Length; for (i = 0; i < l; ++i) { SampleFunction(_grid[i]); } // build mesh from grid samples int vi = 0, ti = 0; for (i = 0; i < l; ++i) { GridSample grid = _grid[i]; float sampleA = grid.sample; float sampleB = _grid[grid.iB].sample; float sampleC = _grid[grid.iC].sample; float sampleD = _grid[grid.iD].sample; // get the index value for this grid position int index = IndexFunction(sampleA, sampleB, sampleD, sampleC); if (index > 0 && index < 15) { // populate vertext and triangle buffers for this grid position Vector3 p = new Vector3(grid.x, grid.y, 0); Vector3[] points = MetaballDefs.pointLookupTable[index]; int[] triangles = MetaballDefs.triangleLookupTable[index]; int j = 0, k = triangles.Length; for (j = 0; j < k; j += 3) { int t1 = triangles[j]; int t2 = triangles[j + 1]; int t3 = triangles[j + 2]; Vector3 p0 = points[t1]; Vector3 p1 = points[t2]; Vector3 p2 = points[t3]; // use SmoothFunction to Lerp points based on sample values p0 = SmoothFunction(p0, sampleA, sampleB, sampleC, sampleD); p1 = SmoothFunction(p1, sampleA, sampleB, sampleC, sampleD); p2 = SmoothFunction(p2, sampleA, sampleB, sampleC, sampleD); _vertices[vi] = p + (p0 * _gridSize); _vertices[vi + 1] = p + (p1 * _gridSize); _vertices[vi + 2] = p + (p2 * _gridSize); _triangles[ti] = vi; _triangles[ti + 1] = vi + 1; _triangles[ti + 2] = vi + 2; vi += 3; ti += 3; } } else if (index == 15) { Vector3 p = new Vector3(grid.x, grid.y, 0); Vector3[] points = MetaballDefs.pointLookupTable[index]; int[] triangles = MetaballDefs.triangleLookupTable[index]; int j = 0, k = triangles.Length; for (j = 0; j < k; j += 3) { Vector3 p0 = points[triangles[j]]; Vector3 p1 = points[triangles[j + 1]]; Vector3 p2 = points[triangles[j + 2]]; _vertices[vi] = p + (p0 * _gridSize); _vertices[vi + 1] = p + (p1 * _gridSize); _vertices[vi + 2] = p + (p2 * _gridSize); _triangles[ti] = vi; _triangles[ti + 1] = vi + 1; _triangles[ti + 2] = vi + 2; vi += 3; ti += 3; } } } // clear unused portion of vertex and triangle buffers System.Array.Clear(_vertices, vi, _verticesLength - vi); System.Array.Clear(_triangles, ti, _trianglesLength - ti); // update mesh _mesh.Clear(false); _mesh.vertices = _vertices; _mesh.triangles = _triangles; }
public override void CreateGeometryForObjects(Device device, ICollection <IAtom> objs, GeomDataBufferStream geomStream, int stream, ref BufferedGeometryData buffer, CompleteOutputDescription coDesc) { // fillable fields int positionPos = -1; int normalPos = -1; int diffusePos = -1; // match field locations for (int i = 0; i < fields.Length; i++) { for (int gf = 0; gf < geomStream.Fields.Length; gf++) { if (fields[i].Format == geomStream.Fields[gf]) { if (fields[i].Usage == "POSITION") { positionPos = geomStream.FieldPositions[gf]; } else if (fields[i].Usage == "NORMAL") { normalPos = geomStream.FieldPositions[gf]; } else if (fields[i].Usage == "DIFFUSE") { diffusePos = geomStream.FieldPositions[gf]; } break; } } } // actually create the metaball triangles or points IVolume[] volumes = new IVolume[objs.Count]; int sIdx = 0; AtomShadingDesc aShading = coDesc.AtomShadingDesc; IMoleculeMaterialLookup lookup = aShading.MoleculeMaterials; foreach (IAtom atom in objs) { IMoleculeMaterialTemplate matTemp = lookup.ResolveBySymbol(atom.Symbol); IMoleculeMaterial material = null; if (matTemp != null) { material = matTemp.BySymbol; } else { PeriodicTableElement pe = (PeriodicTableElement)atom.Properties["PeriodicTableElement"]; if (pe != null) { material = lookup.GetBySeries(pe.ChemicalSerie); } } volumes[sIdx++] = new Metaball(new Vector3((float)atom.X3d, (float)atom.Y3d, (float)atom.Z3d), 0.17f, material.BaseColor); } // process volume into triangles GenericVolumeScene scene = new GenericVolumeScene(volumes); int[] triangles = null; Vector3[] vertices; Color[] colours; Vector3[] normals = null; if (!pointsOnly) { IsosurfaceGenerator3D.GenerateSimpleMesh(scene, new Vector3(), scene.EstimateVolumeMaxSize(), 40, false, out triangles, out vertices, out colours); MeshOptimzer.GenerateTriPointNormals(triangles, vertices, out normals); } else { IsosurfaceGenerator3D.GenerateSimplePointOutline(scene, new Vector3(), scene.EstimateVolumeMaxSize(), 40, out vertices, out colours); } // create buffers buffer = new BufferedGeometryData(device, objs.Count); buffer.vBuffers = new BufferedGeometryData.VertexData[1]; buffer.vBuffers[0] = new BufferedGeometryData.VertexData(); buffer.vBuffers[0].Buffer = new VertexBuffer(device, geomStream.Stride * vertices.Length, Usage.WriteOnly, geomStream.Format, Pool.Managed); buffer.vBuffers[0].Stride = geomStream.Stride; buffer.vBuffers[0].NumElements = vertices.Length; buffer.vBuffers[0].Format = geomStream.Format; buffer.iBuffers = new BufferedGeometryData.IndexData[1]; buffer.iBuffers[0] = new BufferedGeometryData.IndexData(); buffer.iBuffers[0].Desc = BufferedGeometryData.IndexData.Description.Geometry; if (pointsOnly) { buffer.iBuffers[0].NumPrimitives = vertices.Length; buffer.iBuffers[0].PrimType = PrimitiveType.PointList; buffer.Light = false; } else { buffer.iBuffers[0].NumPrimitives = triangles.Length / 3; buffer.iBuffers[0].PrimType = PrimitiveType.TriangleList; buffer.iBuffers[0].Buffer = new IndexBuffer(typeof(int), triangles.Length, device, Usage.WriteOnly, Pool.Managed); } // lock stream GraphicsStream data = buffer.vBuffers[0].Buffer.Lock(0, 0, LockFlags.None); // fill fields int clr = Color.FromArgb(255, 255, 255).ToArgb(); long pos = 0; for (int i = 0; i < vertices.Length; i++) { if (positionPos != -1) { data.Seek(pos + positionPos, SeekOrigin.Begin); data.Write(vertices[i].X); data.Write(vertices[i].Y); data.Write(vertices[i].Z); } if (normalPos != -1 && !pointsOnly) { data.Seek(pos + normalPos, SeekOrigin.Begin); data.Write(normals[i].X); data.Write(normals[i].Y); data.Write(normals[i].Z); } if (diffusePos != -1) { data.Seek(pos + diffusePos, SeekOrigin.Begin); data.Write(colours[i].ToArgb()); } //verts[i].Color = colours[i].ToArgb(); pos += geomStream.Stride; } buffer.vBuffers[0].Buffer.Unlock(); if (!pointsOnly) { buffer.iBuffers[0].Buffer.SetData(triangles, 0, LockFlags.None); } // dispose of temp data }
/**This function should generate the mesh that surrounds all of the metaballs. * Although there may be many metaballs, it will technically only generate a single mesh. **/ void generateMesh() { if (metaballs.Count <= 0) { return; } //STEP 1. Iterate through the cubic region surrounding each metaball. //Determine the largest size of the region to explore. Need to locate the largest and smallest X, Y and Z values. vertices.Clear(); triangles.Clear(); float minX, maxX, minY, maxY, minZ, maxZ; minX = maxX = minY = maxY = minZ = maxZ = 0; for (int i = 0; i < metaballs.Count; i++) { Metaball m = metaballs[i]; Vector3 position = m.transform.position; float r = m.maxSquaredRadius; if ((i == 0) || (position.x - r < minX)) { minX = position.x - r; } if ((i == 0) || (position.x + r > maxX)) { maxX = position.x + r; } if ((i == 0) || (position.y - r < minY)) { minY = position.y - r; } if ((i == 0) || (position.y + r > maxY)) { maxY = position.y + r; } if ((i == 0) || (position.z - r < minZ)) { minZ = position.z - r; } if ((i == 0) || (position.z + r > maxZ)) { maxZ = position.z + r; } } int xResolution = Mathf.CeilToInt((maxX - minX) / cubeLength); int yResolution = Mathf.CeilToInt((maxY - minY) / cubeLength); int zResolution = Mathf.CeilToInt((maxZ - minZ) / cubeLength); Vector3 corner = new Vector3(minX, minY, minZ); for (int x = 0; x < xResolution; x++) { for (int y = 0; y < yResolution; y++) { for (int z = 0; z < zResolution; z++) { //Start in left, bottom, back corner, slowly work your way to the right, top, front corner. //For each cube you will examine 8 points, and determine the value at that point //Each cube is going to need 8 vertices - Order is strange because it needs to fit with the Marching Cubes library //1. Left Bottom Back int i = (x * xResolution * xResolution) + (y * yResolution) + z; float[] cube = new float[8]; cube[0] = getValueAtPoint(new Vector3(corner.x + (cubeLength * x), corner.y + (cubeLength * y), corner.z + (cubeLength * z))); //2. Left Bottom Front cube[4] = getValueAtPoint(new Vector3(corner.x + (cubeLength * x), corner.y + (cubeLength * y), corner.z + (cubeLength * (z + 1)))); //3. Left Top Back cube[3] = getValueAtPoint(new Vector3(corner.x + (cubeLength * x), corner.y + (cubeLength * (y + 1)), corner.z + (cubeLength * z))); //4. Left Top Front cube[7] = getValueAtPoint(new Vector3(corner.x + (cubeLength * x), corner.y + (cubeLength * (y + 1)), corner.z + (cubeLength * (z + 1)))); //5. Right Bottom Back cube[1] = getValueAtPoint(new Vector3(corner.x + (cubeLength * (x + 1)), corner.y + (cubeLength * y), corner.z + (cubeLength * z))); //6. Right Bottom Front cube[5] = getValueAtPoint(new Vector3(corner.x + (cubeLength * (x + 1)), corner.y + (cubeLength * y), corner.z + (cubeLength * (z + 1)))); //7. Right Top Back cube[2] = getValueAtPoint(new Vector3(corner.x + (cubeLength * (x + 1)), corner.y + (cubeLength * (y + 1)), corner.z + (cubeLength * z))); //8. Right Top Front cube[6] = getValueAtPoint(new Vector3(corner.x + (cubeLength * (x + 1)), corner.y + (cubeLength * (y + 1)), corner.z + (cubeLength * (z + 1)))); marching.March(corner.x + (cubeLength * x), corner.y + (cubeLength * y), corner.z + (cubeLength * z), cube, cubeLength, vertices, triangles); } } } mesh.Clear(); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); }