// creates a random, fractal heightfield public void SetFractal(byte[] grid, int gridIndex, int bytesPerElement, PHY_ScalarType type, int step) { Debug.Assert(grid != null); Debug.Assert(bytesPerElement > 0); Debug.Assert(step > 0); Debug.Assert(step < s_gridSize); int newStep = step / 2; // std::cerr << "Computing grid with step = " << step << ": before\n"; // dumpGrid(grid, bytesPerElement, type, step + 1); // special case: starting (must set four corners) if (s_gridSize - 1 == step) { // pick a non-zero (possibly negative) base elevation for testing float baseValue = RandomHeight(step / 2); ConvertFromFloat(grid, gridIndex, baseValue, type); ConvertFromFloat(grid, gridIndex + step * bytesPerElement, baseValue, type); ConvertFromFloat(grid, gridIndex + step * s_gridSize * bytesPerElement, baseValue, type); ConvertFromFloat(grid, gridIndex + (step * s_gridSize + step) * bytesPerElement, baseValue, type); } // determine elevation of each corner float c00 = ConvertToFloat(grid, gridIndex, type); float c01 = ConvertToFloat(grid, gridIndex + step * bytesPerElement, type); float c10 = ConvertToFloat(grid, gridIndex + (step * s_gridSize) * bytesPerElement, type); float c11 = ConvertToFloat(grid, gridIndex + (step * s_gridSize + step) * bytesPerElement, type); // set top middle UpdateHeight(grid, gridIndex + newStep * bytesPerElement, 0.5f * (c00 + c01) + RandomHeight(step), type); // set left middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize) * bytesPerElement, 0.5f * (c00 + c10) + RandomHeight(step), type); // set right middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize + step) * bytesPerElement, 0.5f * (c01 + c11) + RandomHeight(step), type); // set bottom middle UpdateHeight(grid, gridIndex + (step * s_gridSize + newStep) * bytesPerElement, 0.5f * (c10 + c11) + RandomHeight(step), type); // set middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize + newStep) * bytesPerElement, 0.25f * (c00 + c01 + c10 + c11) + RandomHeight(step), type); // std::cerr << "Computing grid with step = " << step << ": after\n"; // dumpGrid(grid, bytesPerElement, type, step + 1); // terminate? if (newStep < 2) { return; } // recurse SetFractal(grid, gridIndex, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + newStep * bytesPerElement, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + (newStep * s_gridSize) * bytesPerElement, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + ((newStep * s_gridSize) + newStep) * bytesPerElement, bytesPerElement, type, newStep); }
public static void ConvertFromFloat(byte[] p, int pindex, float value, PHY_ScalarType type) { Debug.Assert(p != null, "null"); switch (type) { case PHY_ScalarType.PHY_FLOAT: { byte[] temp = BitConverter.GetBytes(value); Array.Copy(temp, 0, p, pindex, temp.Length); } break; case PHY_ScalarType.PHY_UCHAR: { p[pindex] = (byte)(value / s_gridHeightScale); } break; case PHY_ScalarType.PHY_SHORT: { short temp = (short)(value / s_gridHeightScale); byte[] temp2 = BitConverter.GetBytes(temp); Array.Copy(temp2, 0, p, pindex, temp2.Length); } break; default: Debug.Assert(false, "bad type"); break; } }
public float ConvertToFloat(byte[] p, int pindex, PHY_ScalarType type) { Debug.Assert(p != null); switch (type) { case PHY_ScalarType.PHY_FLOAT: { return(BitConverter.ToSingle(p, pindex)); } case PHY_ScalarType.PHY_UCHAR: { return(p[pindex] * s_gridHeightScale); } case PHY_ScalarType.PHY_SHORT: { short temp = BitConverter.ToInt16(p, pindex); return((temp) * s_gridHeightScale); } default: Debug.Assert(false, "bad type"); break; } return(0); }
// TODO: it would probably cleaner to have a struct per data type, so // you could lookup byte sizes, conversion functions, etc. public int GetByteSize(PHY_ScalarType type) { int size = 0; switch (type) { case PHY_ScalarType.PHY_FLOAT: size = 4; break; case PHY_ScalarType.PHY_UCHAR: size = 1; break; case PHY_ScalarType.PHY_SHORT: size = 2; break; default: Debug.Assert(false, "Bad heightfield data type"); break; } return(size); }
public void UpdateHeight(byte[] p, int index, float new_val, PHY_ScalarType type) { float old_val = ConvertToFloat(p, index, type); //if (old_val != 0.0f) { ConvertFromFloat(p, index, new_val, type); } }
/// preferred constructor /** * This constructor supports a range of heightfield * data types, and allows for a non-zero minimum height value. * heightScale is needed for any integer-based heightfield data types. */ public HeightfieldTerrainShape(int heightStickWidth, int heightStickLength, byte[] heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges) { Initialize(heightStickWidth, heightStickLength, heightfieldData, heightScale, minHeight, maxHeight, upAxis, heightDataType, flipQuadEdges); }
public void addIndexedMesh(btIndexedMesh mesh, PHY_ScalarType indexType) { BulletPINVOKE.btTriangleIndexVertexArray_addIndexedMesh__SWIG_0(swigCPtr, btIndexedMesh.getCPtr(mesh), (int)indexType); if (BulletPINVOKE.SWIGPendingException.Pending) { throw BulletPINVOKE.SWIGPendingException.Retrieve(); } }
public void SetRadial(byte[] grid, int bytesPerElement, PHY_ScalarType type, float phase) { Debug.Assert(grid != null); Debug.Assert(bytesPerElement > 0); // min/max float period = 0.5f / s_gridSpacing; float floor = 0.0f; float min_r = (float)(3.0f * Math.Sqrt(s_gridSpacing)); float magnitude = (float)(50.0f * Math.Sqrt(s_gridSpacing)); // pick a base_phase such that phase = 0 results in max height // (this way, if you create a heightfield with phase = 0, // you can rely on the min/max heights that result) float base_phase = (0.5f * MathUtil.SIMD_PI) - (period * min_r); phase += base_phase; // center of grid float cx = 0.5f * s_gridSize * s_gridSpacing; float cy = cx; // assume square grid byte[] p = grid; int pindex = 0; for (int i = 0; i < s_gridSize; ++i) { float x = i * s_gridSpacing; for (int j = 0; j < s_gridSize; ++j) { float y = j * s_gridSpacing; float dx = x - cx; float dy = y - cy; float r = (float)Math.Sqrt((dx * dx) + (dy * dy)); float z = period; if (r < min_r) { r = min_r; } z = (float)((1.0f / r) * Math.Sin(period * r + phase)); if (z > period) { z = period; } else if (z < -period) { z = -period; } z = floor + magnitude * z; ConvertFromFloat(p, pindex, z, type); pindex += bytesPerElement; } } }
public PHY_ScalarType NextType(PHY_ScalarType type) { switch (type) { case PHY_ScalarType.PHY_FLOAT: return(PHY_ScalarType.PHY_SHORT); case PHY_ScalarType.PHY_SHORT: return(PHY_ScalarType.PHY_UCHAR); case PHY_ScalarType.PHY_UCHAR: return(PHY_ScalarType.PHY_FLOAT); } return(PHY_ScalarType.PHY_FLOAT); }
/// legacy constructor /** * The legacy constructor assumes the heightfield has a minimum height * of zero. Only unsigned char or floats are supported. For legacy * compatibility reasons, heightScale is calculated as maxHeight / 65535 * (and is only used when useFloatData = false). */ public HeightfieldTerrainShape(int heightStickWidth, int heightStickLength, byte[] heightfieldData, float maxHeight, int upAxis, bool useFloatData, bool flipQuadEdges) { // legacy constructor: support only float or unsigned char, // and min height is zero PHY_ScalarType hdt = (useFloatData) ? PHY_ScalarType.PHY_FLOAT : PHY_ScalarType.PHY_UCHAR; float minHeight = 0.0f; // previously, height = uchar * maxHeight / 65535. // So to preserve legacy behavior, heightScale = maxHeight / 65535 float heightScale = maxHeight / 65535; Initialize(heightStickWidth, heightStickLength, heightfieldData, heightScale, minHeight, maxHeight, upAxis, hdt, flipQuadEdges); }
public void DumpGrid(byte[] grid, int bytesPerElement, PHY_ScalarType type, int max) { //std::cerr << "Grid:\n"; for (int j = 0; j < max; ++j) { for (int i = 0; i < max; ++i) { long offset = j * s_gridSize + i; float z = ConvertToFloat(grid, (int)(offset * bytesPerElement), type); //sprintf(buffer, "%6.2f", z); //std::cerr << " " << buffer; } //std::cerr << "\n"; } }
public override void GetLockedVertexIndexBase(out Object vertexbase, out int numverts, out PHY_ScalarType type, out int vertexStride, out Object indexbase, out int indexstride, out int numfaces, out PHY_ScalarType indicestype, int subpart) { Debug.Assert(subpart< GetNumSubParts() ); IndexedMesh mesh = m_indexedMeshes[subpart]; numverts = mesh.m_numVertices; vertexbase = mesh.m_vertexBase; type = PHY_ScalarType.PHY_FLOAT; vertexStride = mesh.m_vertexStride; numfaces = mesh.m_numTriangles; indexbase = mesh.m_triangleIndexBase; indexstride = mesh.m_triangleIndexStride; indicestype = mesh.m_indexType; }
public float GetGridHeight(byte[] grid, int i, int j, PHY_ScalarType type) { Debug.Assert(grid != null); Debug.Assert(i >= 0 && i < s_gridSize); Debug.Assert(j >= 0 && j < s_gridSize); int bpe = GetByteSize(type); Debug.Assert(bpe > 0, "bad bytes per element"); int idx = (j * s_gridSize) + i; int offset = ((int)bpe) * idx; //byte_t* p = grid + offset; return(ConvertToFloat(grid, offset, type)); }
public String GetDataTypeName(PHY_ScalarType type) { switch (type) { case PHY_ScalarType.PHY_UCHAR: return("UnsignedChar"); case PHY_ScalarType.PHY_SHORT: return("Short"); case PHY_ScalarType.PHY_FLOAT: return("Float"); default: Debug.Assert(false, "bad heightfield data type"); break; } return(null); }
public String GetDataTypeName(PHY_ScalarType type) { switch (type) { case PHY_ScalarType.PHY_UCHAR: return "UnsignedChar"; case PHY_ScalarType.PHY_SHORT: return "Short"; case PHY_ScalarType.PHY_FLOAT: return "Float"; default: Debug.Assert(false,"bad heightfield data type"); break; } return null; }
public abstract void GetLockedReadOnlyVertexIndexBase(out Object vertexbase, out int numverts, out PHY_ScalarType type, out int stride, out Object indexbase, out int indexstride, out int numfaces, out PHY_ScalarType indicestype, int subpart);
public PHY_ScalarType NextType(PHY_ScalarType type) { switch (type) { case PHY_ScalarType.PHY_FLOAT: return PHY_ScalarType.PHY_SHORT; case PHY_ScalarType.PHY_SHORT: return PHY_ScalarType.PHY_UCHAR; case PHY_ScalarType.PHY_UCHAR: return PHY_ScalarType.PHY_FLOAT; } return PHY_ScalarType.PHY_FLOAT; }
public byte[] GetRawHeightfieldData(eTerrainModel model,PHY_ScalarType type,ref float minHeight,ref float maxHeight) { // std::cerr << "\nRegenerating terrain\n"; // std::cerr << " model = " << model << "\n"; // std::cerr << " type = " << type << "\n"; long nElements = ((long) s_gridSize) * s_gridSize; // std::cerr << " nElements = " << nElements << "\n"; int bytesPerElement = GetByteSize(type); // std::cerr << " bytesPerElement = " << bytesPerElement << "\n"; Debug.Assert(bytesPerElement > 0,"bad bytes per element"); long nBytes = nElements * bytesPerElement; // std::cerr << " nBytes = " << nBytes << "\n"; byte[] raw = new byte[nBytes]; Debug.Assert(raw != null ,"out of memory"); // reseed randomization every 30 seconds // srand(time(NULL) / 30); // populate based on model switch (model) { case eTerrainModel.eRadial: SetRadial(raw, bytesPerElement, type); break; case eTerrainModel.eFractal: for (int i = 0; i < nBytes; i++) { raw[i] = 0; } SetFractal(raw,0, bytesPerElement, type, s_gridSize - 1); break; default: Debug.Assert(false,"bad model type"); break; } if (false) { // inside if(0) so it keeps compiling but isn't // exercised and doesn't cause warnings // std::cerr << "final grid:\n"; DumpGrid(raw,bytesPerElement, type, s_gridSize - 1); } // find min/max for (int i = 0; i < s_gridSize; ++i) { for (int j = 0; j < s_gridSize; ++j) { float z = GetGridHeight(raw, i, j, type); // std::cerr << "i=" << i << ", j=" << j << ": z=" << z << "\n"; // update min/max if (i == 0 && j == 0) { minHeight = z; maxHeight = z; } else { if (z < minHeight) { minHeight = z; } if (z > maxHeight) { maxHeight = z; } } } } if (maxHeight < -minHeight) { maxHeight = -minHeight; } if (minHeight > -maxHeight) { minHeight = -maxHeight; } // std::cerr << " minHeight = " << minHeight << "\n"; // std::cerr << " maxHeight = " << maxHeight << "\n"; return raw; }
public void UpdateHeight(byte[] p,int index,float new_val,PHY_ScalarType type) { float old_val = ConvertToFloat(p,index, type); //if (old_val != 0.0f) { ConvertFromFloat(p, index,new_val, type); } }
// creates a radially-varying heightfield public void SetRadial(byte[] grid,int bytesPerElement,PHY_ScalarType type) { SetRadial(grid,bytesPerElement,type,0.0f); }
public float GetGridHeight(byte[] grid,int i,int j,PHY_ScalarType type) { Debug.Assert(grid != null); Debug.Assert(i >= 0 && i < s_gridSize); Debug.Assert(j >= 0 && j < s_gridSize); int bpe = GetByteSize(type); Debug.Assert(bpe > 0,"bad bytes per element"); int idx = (j * s_gridSize) + i; int offset = ((int)bpe) * idx; //byte_t* p = grid + offset; return ConvertToFloat(grid,offset, type); }
/// public initialization /** * Handles the work of constructors so that public constructors can be * backwards-compatible without a lot of copy/paste. */ public void Initialize(int heightStickWidth, int heightStickLength, Object heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, PHY_ScalarType hdt, bool flipQuadEdges) { // validation Debug.Assert(heightStickWidth > 1, "bad width"); Debug.Assert(heightStickLength > 1, "bad length"); Debug.Assert(heightfieldData != null, "null heightfield data"); // Debug.Assert(heightScale) -- do we care? Trust caller here Debug.Assert(minHeight <= maxHeight, "bad min/max height"); Debug.Assert(upAxis >= 0 && upAxis < 3, "bad upAxis--should be in range [0,2]"); Debug.Assert(hdt != PHY_ScalarType.PHY_UCHAR || hdt != PHY_ScalarType.PHY_FLOAT || hdt != PHY_ScalarType.PHY_SHORT, "Bad height data type enum"); // initialize member variables m_shapeType = BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE; m_heightStickWidth = heightStickWidth; m_heightStickLength = heightStickLength; m_minHeight = minHeight; m_maxHeight = maxHeight; m_width = (heightStickWidth - 1); m_length = (heightStickLength - 1); m_heightScale = heightScale; // copy the data in m_heightFieldDataByte = heightfieldData as byte[]; m_heightFieldDataFloat = heightfieldData as float[]; m_heightDataType = hdt; m_flipQuadEdges = flipQuadEdges; m_useDiamondSubdivision = false; m_upAxis = upAxis; m_localScaling = new IndexedVector3(1f); // determine min/max axis-aligned bounding box (aabb) values switch (m_upAxis) { case 0: { m_localAabbMin = new IndexedVector3(m_minHeight, 0, 0); m_localAabbMax = new IndexedVector3(m_maxHeight, m_width, m_length); break; } case 1: { m_localAabbMin = new IndexedVector3(0, m_minHeight, 0); m_localAabbMax = new IndexedVector3(m_width, m_maxHeight, m_length); break; }; case 2: { m_localAabbMin = new IndexedVector3(0, 0, m_minHeight); m_localAabbMax = new IndexedVector3(m_width, m_length, m_maxHeight); break; } default: { //need to get valid m_upAxis Debug.Assert(false, "Bad m_upAxis"); break; } } // remember origin (defined as exact middle of aabb) m_localOrigin = 0.5f * (m_localAabbMin + m_localAabbMax); }
// creates a radially-varying heightfield public void SetRadial(byte[] grid, int bytesPerElement, PHY_ScalarType type) { SetRadial(grid, bytesPerElement, type, 0.0f); }
public abstract void getLockedReadOnlyVertexIndexBase(out Object vertexbase, out int numverts, out PHY_ScalarType type, out int stride, out Object indexbase, out int indexstride, out int numfaces, out PHY_ScalarType indicestype, int subpart);
public virtual void InternalProcessAllTriangles(IInternalTriangleIndexCallback callback, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax) { int numtotalphysicsverts = 0; int part, graphicssubparts = GetNumSubParts(); Object vertexbase = null; Object indexbase = null; int indexstride = 3; PHY_ScalarType type = PHY_ScalarType.PHY_FLOAT; PHY_ScalarType gfxindextype = PHY_ScalarType.PHY_INTEGER; int stride = 0, numverts = 0, numtriangles = 0; IndexedVector3[] triangle = new IndexedVector3[3]; IndexedVector3 meshScaling = GetScaling(); ///if the number of parts is big, the performance might drop due to the innerloop switch on indextype for (part = 0; part < graphicssubparts; part++) { GetLockedReadOnlyVertexIndexBase(out vertexbase, out numverts, out type, out stride, out indexbase, out indexstride, out numtriangles, out gfxindextype, part); numtotalphysicsverts += numtriangles * 3; //upper bound switch (gfxindextype) { case PHY_ScalarType.PHY_INTEGER: { int[] indexList = ((ObjectArray <int>)indexbase).GetRawArray(); if (vertexbase is ObjectArray <IndexedVector3> ) { IndexedVector3[] vertexList = (vertexbase as ObjectArray <IndexedVector3>).GetRawArray(); //string filename = "c:/tmp/xna-bvh-mesh-iv3.txt"; //FileStream filestream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read); //using (StreamWriter writer = new StreamWriter(filestream)) //{ //writer.WriteLine("XNA IV3"); for (int gfxindex = 0; gfxindex < numtriangles; gfxindex++) { int triIndex = (gfxindex * indexstride); //writer.WriteLine(String.Format("indices[{0}][{1}][{2}]", indexList[triIndex], indexList[triIndex + 1], indexList[triIndex + 2])); int index1 = indexList[triIndex]; int index2 = indexList[triIndex + 1]; int index3 = indexList[triIndex + 2]; triangle[0] = vertexList[index1] * meshScaling; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", triangle[0].X,triangle[0].Y,triangle[0].Z)); triangle[1] = vertexList[index2] * meshScaling; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", triangle[1].X, triangle[1].Y, triangle[1].Z)); triangle[2] = vertexList[index3] * meshScaling; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", triangle[2].X, triangle[2].Y, triangle[2].Z)); //writer.WriteLine(String.Format("index[{0}] triangle[0].X =[{1:0.00000000}] triangle[0].Y =[{2:0.00000000}] triangle[0].Z =[{3:0.00000000}]", gfxindex, triangle[0].X, triangle[0].Y, triangle[0].Z)); //writer.WriteLine(String.Format("index[{0}] triangle[1].X =[{1:0.00000000}] triangle[1].Y =[{2:0.00000000}] triangle[1].Z =[{3:0.00000000}]", gfxindex, triangle[1].X, triangle[1].Y, triangle[1].Z)); //writer.WriteLine(String.Format("index[{0}] triangle[2].X =[{1:0.00000000}] triangle[2].Y =[{2:0.00000000}] triangle[2].Z =[{3:0.00000000}]", gfxindex, triangle[2].X, triangle[2].Y, triangle[2].Z)); //if(BulletGlobals.g_streamWriter != null && BulletGlobals.debugStridingMesh && !callback.graphics()) //{ // MathUtil.PrintVector3(BulletGlobals.g_streamWriter,"SMI:T0",triangle[0]); // MathUtil.PrintVector3(BulletGlobals.g_streamWriter, "SMI:T1", triangle[1]); // MathUtil.PrintVector3(BulletGlobals.g_streamWriter, "SMI:T2", triangle[2]); //} callback.InternalProcessTriangleIndex(triangle, part, gfxindex); } // writer.Flush(); //} } else if (vertexbase is ObjectArray <Microsoft.Xna.Framework.Vector3> ) { Microsoft.Xna.Framework.Vector3[] vertexList = (vertexbase as ObjectArray <Microsoft.Xna.Framework.Vector3>).GetRawArray(); for (int gfxindex = 0; gfxindex < numtriangles; gfxindex++) { int triIndex = (gfxindex * indexstride); int index1 = indexList[triIndex]; int index2 = indexList[triIndex + 1]; int index3 = indexList[triIndex + 2]; triangle[0] = new IndexedVector3(vertexList[index1]) * meshScaling; triangle[1] = new IndexedVector3(vertexList[index2]) * meshScaling; triangle[2] = new IndexedVector3(vertexList[index3]) * meshScaling; //if (BulletGlobals.g_streamWriter != null && BulletGlobals.debugStridingMesh && !callback.graphics()) //{ // MathUtil.PrintVector3(BulletGlobals.g_streamWriter, "SMI:T0", triangle[0]); // MathUtil.PrintVector3(BulletGlobals.g_streamWriter, "SMI:T1", triangle[1]); // MathUtil.PrintVector3(BulletGlobals.g_streamWriter, "SMI:T2", triangle[2]); //} callback.InternalProcessTriangleIndex(triangle, part, gfxindex); } } else if (vertexbase is ObjectArray <float> ) { //string filename = "c:/tmp/xna-bvh-mesh-float.txt"; //FileStream filestream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read); //using (StreamWriter writer = new StreamWriter(filestream)) //{ //writer.WriteLine("XNA FLOAT"); float[] vertexList = (vertexbase as ObjectArray <float>).GetRawArray(); for (int gfxindex = 0; gfxindex < numtriangles; gfxindex++) { int triIndex = (gfxindex * indexstride); //writer.WriteLine(String.Format("indices[{0}][{1}][{2}]", indexList[triIndex], indexList[triIndex + 1], indexList[triIndex + 2])); // ugly!! int index1 = indexList[triIndex]; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2])); triangle[0] = new IndexedVector3(vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2]) * meshScaling; index1 = indexList[triIndex + 1]; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2])); triangle[1] = new IndexedVector3(vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2]) * meshScaling; index1 = indexList[triIndex + 2]; //writer.WriteLine(String.Format("GB1[{0:0.00000000}][{1:0.00000000}][{2:0.00000000}]", vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2])); triangle[2] = new IndexedVector3(vertexList[index1 * stride], vertexList[(index1 * stride) + 1], vertexList[(index1 * stride) + 2]) * meshScaling; //writer.WriteLine(String.Format("index[{0}] triangle[0].X =[{1:0.00000000}] triangle[0].Y =[{2:0.00000000}] triangle[0].Z =[{3:0.00000000}]", gfxindex, triangle[0].X, triangle[0].Y, triangle[0].Z)); //writer.WriteLine(String.Format("index[{0}] triangle[1].X =[{1:0.00000000}] triangle[1].Y =[{2:0.00000000}] triangle[1].Z =[{3:0.00000000}]", gfxindex, triangle[1].X, triangle[1].Y, triangle[1].Z)); //writer.WriteLine(String.Format("index[{0}] triangle[2].X =[{1:0.00000000}] triangle[2].Y =[{2:0.00000000}] triangle[2].Z =[{3:0.00000000}]", gfxindex, triangle[2].X, triangle[2].Y, triangle[2].Z)); callback.InternalProcessTriangleIndex(triangle, part, gfxindex); } //writer.Flush(); //} } else { Debug.Assert(false); // unsupported type .... } break; } default: { Debug.Assert(gfxindextype == PHY_ScalarType.PHY_INTEGER); break; } } UnLockReadOnlyVertexBase(part); } }
// TODO: it would probably cleaner to have a struct per data type, so // you could lookup byte sizes, conversion functions, etc. public int GetByteSize(PHY_ScalarType type) { int size = 0; switch (type) { case PHY_ScalarType.PHY_FLOAT: size = 4; break; case PHY_ScalarType.PHY_UCHAR: size = 1; break; case PHY_ScalarType.PHY_SHORT: size = 2; break; default: Debug.Assert(false,"Bad heightfield data type"); break; } return size; }
///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// public static void GenerateInternalEdgeInfo(BvhTriangleMeshShape trimeshShape, TriangleInfoMap triangleInfoMap) { //the user pointer shouldn't already be used for other purposes, we intend to store connectivity info there! if (trimeshShape.GetTriangleInfoMap() != null) { return; } trimeshShape.SetTriangleInfoMap(triangleInfoMap); StridingMeshInterface meshInterface = trimeshShape.GetMeshInterface(); IndexedVector3 meshScaling = meshInterface.GetScaling(); for (int partId = 0; partId < meshInterface.GetNumSubParts(); partId++) { object vertexbase = null; int numverts = 0; PHY_ScalarType type = PHY_ScalarType.PHY_INTEGER; int stride = 0; object indexbase = null; int indexstride = 0; int numfaces = 0; PHY_ScalarType indicestype = PHY_ScalarType.PHY_INTEGER; //PHY_ScalarType indexType=0; IndexedVector3[] triangleVerts = new IndexedVector3[3]; meshInterface.GetLockedReadOnlyVertexIndexBase(out vertexbase, out numverts, out type, out stride, out indexbase, out indexstride, out numfaces, out indicestype, partId); IndexedVector3 aabbMin, aabbMax; switch (indicestype) { case PHY_ScalarType.PHY_INTEGER: { int[] indexList = ((ObjectArray <int>)indexbase).GetRawArray(); if (vertexbase is ObjectArray <IndexedVector3> ) { IndexedVector3[] vertexList = (vertexbase as ObjectArray <IndexedVector3>).GetRawArray(); int indexCounter = 0; for (int triangleIndex = 0; triangleIndex < numfaces; triangleIndex++) { int index1 = indexList[triangleIndex]; int index2 = indexList[triangleIndex + 1]; int index3 = indexList[triangleIndex + 2]; triangleVerts[0] = new IndexedVector3(vertexList[index1]) * meshScaling; triangleVerts[1] = new IndexedVector3(vertexList[index2]) * meshScaling; triangleVerts[2] = new IndexedVector3(vertexList[index3]) * meshScaling; ProcessResult(triangleVerts, out aabbMin, out aabbMax, trimeshShape, partId, triangleIndex, triangleInfoMap); } } #if XNA else if (vertexbase is ObjectArray <Microsoft.Xna.Framework.Vector3> ) { Microsoft.Xna.Framework.Vector3[] vertexList = (vertexbase as ObjectArray <Microsoft.Xna.Framework.Vector3>).GetRawArray(); int indexCounter = 0; for (int triangleIndex = 0; triangleIndex < numfaces; triangleIndex++) { int index1 = indexList[triangleIndex]; int index2 = indexList[triangleIndex + 1]; int index3 = indexList[triangleIndex + 2]; triangleVerts[0] = new IndexedVector3(vertexList[index1]) * meshScaling; triangleVerts[1] = new IndexedVector3(vertexList[index2]) * meshScaling; triangleVerts[2] = new IndexedVector3(vertexList[index3]) * meshScaling; ProcessResult(triangleVerts, out aabbMin, out aabbMax, trimeshShape, partId, triangleIndex, triangleInfoMap); } } #endif else if (vertexbase is ObjectArray <float> ) { float[] vertexList = (vertexbase as ObjectArray <float>).GetRawArray(); for (int triangleIndex = 0; triangleIndex < numfaces; triangleIndex++) { triangleVerts[0] = new IndexedVector3(vertexList[indexList[triangleIndex]], vertexList[indexList[triangleIndex] + 1], vertexList[indexList[triangleIndex] + 2]) * meshScaling; triangleVerts[1] = new IndexedVector3(vertexList[indexList[triangleIndex + 1]], vertexList[indexList[triangleIndex + 1] + 1], vertexList[indexList[triangleIndex + 1] + 2]) * meshScaling; triangleVerts[2] = new IndexedVector3(vertexList[indexList[triangleIndex + 2]], vertexList[indexList[triangleIndex + 2] + 1], vertexList[indexList[triangleIndex + 2] + 2]) * meshScaling; ProcessResult(triangleVerts, out aabbMin, out aabbMax, trimeshShape, partId, triangleIndex, triangleInfoMap); } } break; } default: { Debug.Assert(indicestype == PHY_ScalarType.PHY_INTEGER); break; } } } }
public float ConvertToFloat(byte[] p,int pindex,PHY_ScalarType type) { Debug.Assert(p != null); switch (type) { case PHY_ScalarType.PHY_FLOAT: { return BitConverter.ToSingle(p,pindex); } case PHY_ScalarType.PHY_UCHAR: { return p[pindex] * s_gridHeightScale; } case PHY_ScalarType.PHY_SHORT: { short temp = BitConverter.ToInt16(p, pindex); return ((temp) * s_gridHeightScale); } default: Debug.Assert(false,"bad type"); break; } return 0; }
public void UpdateBvhNodes(StridingMeshInterface meshInterface, int firstNode, int endNode, int index) { //(void)index; Debug.Assert(m_useQuantization); int curNodeSubPart = -1; //get access info to trianglemesh data Object vertexBaseObject = null; int numverts = 0; PHY_ScalarType type = PHY_ScalarType.PHY_INTEGER; int stride = 0; Object indexBaseObject = null; int indexstride = 0; int numfaces = 0; PHY_ScalarType indicestype = PHY_ScalarType.PHY_INTEGER; IndexedVector3[] triangleVerts = new IndexedVector3[3]; IndexedVector3 aabbMin, aabbMax; IndexedVector3 meshScaling = meshInterface.GetScaling(); for (int i = endNode - 1; i >= firstNode; i--) { QuantizedBvhNode curNode = m_quantizedContiguousNodes[i]; if (curNode.IsLeafNode()) { //recalc aabb from triangle data int nodeSubPart = curNode.GetPartId(); int nodeTriangleIndex = curNode.GetTriangleIndex(); if (nodeSubPart != curNodeSubPart) { if (curNodeSubPart >= 0) { meshInterface.UnLockReadOnlyVertexBase(curNodeSubPart); } meshInterface.GetLockedReadOnlyVertexIndexBase(out vertexBaseObject, out numverts, out type, out stride, out indexBaseObject, out indexstride, out numfaces, out indicestype, nodeSubPart); curNodeSubPart = nodeSubPart; Debug.Assert(indicestype == PHY_ScalarType.PHY_INTEGER); } //triangles.getLockedReadOnlyVertexIndexBase(vertexBase,numVerts, int gfxBaseIndex = nodeTriangleIndex * indexstride; //unsigned int* gfxbase = (unsigned int*)(indexbase+nodeTriangleIndex*indexstride); int[] indexBase = (indexBaseObject as ObjectArray <int>).GetRawArray(); if (vertexBaseObject is IList <IndexedVector3> ) { IndexedVector3[] vertexBase = (vertexBaseObject as ObjectArray <IndexedVector3>).GetRawArray(); for (int j = 2; j >= 0; j--) { int graphicsIndex = indexBase[gfxBaseIndex + j]; if (type == PHY_ScalarType.PHY_FLOAT) { triangleVerts[j] = vertexBase[graphicsIndex] * meshScaling; } else { Debug.Assert(false, "Unsupported Type"); } } } else if (vertexBaseObject is ObjectArray <float> ) { float[] vertexBase = (vertexBaseObject as ObjectArray <float>).GetRawArray(); for (int j = 2; j >= 0; j--) { //int graphicsindex = indicestype==PHY_ScalarType.PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; int graphicsIndex = indexBase[gfxBaseIndex + j]; if (type == PHY_ScalarType.PHY_FLOAT) { int graphicsBaseIndex = (graphicsIndex * stride); //IList<float> graphicsbase = (float*)(vertexbase+graphicsindex*stride); triangleVerts[j] = new IndexedVector3( vertexBase[graphicsBaseIndex] * meshScaling.X, vertexBase[graphicsBaseIndex + 1] * meshScaling.Y, vertexBase[graphicsBaseIndex + 2] * meshScaling.Z); } else { Debug.Assert(false, "Unsupported Type"); } } } else { Debug.Assert(false, "Unsupported Type"); } aabbMin = MathUtil.MAX_VECTOR; aabbMax = MathUtil.MIN_VECTOR; MathUtil.VectorMin(ref triangleVerts[0], ref aabbMin); MathUtil.VectorMax(ref triangleVerts[0], ref aabbMax); MathUtil.VectorMin(ref triangleVerts[1], ref aabbMin); MathUtil.VectorMax(ref triangleVerts[1], ref aabbMax); MathUtil.VectorMin(ref triangleVerts[2], ref aabbMin); MathUtil.VectorMax(ref triangleVerts[2], ref aabbMax); Quantize(out curNode.m_quantizedAabbMin, ref aabbMin, false); Quantize(out curNode.m_quantizedAabbMax, ref aabbMax, true); } else { //combine aabb from both children QuantizedBvhNode leftChildNode = m_quantizedContiguousNodes[i + 1]; QuantizedBvhNode rightChildNode = leftChildNode.IsLeafNode() ? m_quantizedContiguousNodes[i + 2] : m_quantizedContiguousNodes[i + 1 + leftChildNode.GetEscapeIndex()]; { curNode.m_quantizedAabbMin = leftChildNode.m_quantizedAabbMin; curNode.m_quantizedAabbMin.Min(ref rightChildNode.m_quantizedAabbMin); curNode.m_quantizedAabbMax = leftChildNode.m_quantizedAabbMax; curNode.m_quantizedAabbMax.Max(ref rightChildNode.m_quantizedAabbMax); } } } if (curNodeSubPart >= 0) { meshInterface.UnLockReadOnlyVertexBase(curNodeSubPart); } }
public static void ConvertFromFloat(byte[] p,int pindex, float value, PHY_ScalarType type) { Debug.Assert(p != null ,"null"); switch (type) { case PHY_ScalarType.PHY_FLOAT: { byte[] temp = BitConverter.GetBytes(value); Array.Copy(temp,0,p,pindex,temp.Length); } break; case PHY_ScalarType.PHY_UCHAR: { p[pindex] = (byte) (value / s_gridHeightScale); } break; case PHY_ScalarType.PHY_SHORT: { short temp = (short) (value / s_gridHeightScale); byte[] temp2 = BitConverter.GetBytes(temp); Array.Copy(temp2, 0, p, pindex, temp2.Length); } break; default: Debug.Assert(false,"bad type"); break; } }
public void AddIndexedMesh(IndexedMesh mesh, PHY_ScalarType indexType) { m_indexedMeshes.Add(mesh); m_indexedMeshes[m_indexedMeshes.Count - 1].m_indexType = indexType; }
public void SetRadial(byte[] grid,int bytesPerElement,PHY_ScalarType type,float phase) { Debug.Assert(grid != null); Debug.Assert(bytesPerElement > 0); // min/max float period = 0.5f / s_gridSpacing; float floor = 0.0f; float min_r = (float)(3.0f * Math.Sqrt(s_gridSpacing)); float magnitude = (float)(50.0f * Math.Sqrt(s_gridSpacing)); // pick a base_phase such that phase = 0 results in max height // (this way, if you create a heightfield with phase = 0, // you can rely on the min/max heights that result) float base_phase = (0.5f * MathUtil.SIMD_PI) - (period * min_r); phase += base_phase; // center of grid float cx = 0.5f * s_gridSize * s_gridSpacing; float cy = cx; // assume square grid byte[] p = grid; int pindex = 0; for (int i = 0; i < s_gridSize; ++i) { float x = i * s_gridSpacing; for (int j = 0; j < s_gridSize; ++j) { float y = j * s_gridSpacing; float dx = x - cx; float dy = y - cy; float r = (float)Math.Sqrt((dx * dx) + (dy * dy)); float z = period; if (r < min_r) { r = min_r; } z = (float)((1.0f / r) * Math.Sin(period * r + phase)); if (z > period) { z = period; } else if (z < -period) { z = -period; } z = floor + magnitude * z; ConvertFromFloat(p,pindex, z, type); pindex += bytesPerElement; } } }
public override void GetLockedVertexIndexBase(out Object vertexbase, out int numverts, out PHY_ScalarType type, out int vertexStride, out Object indexbase, out int indexstride, out int numfaces, out PHY_ScalarType indicestype, int subpart) { Debug.Assert(subpart < GetNumSubParts()); IndexedMesh mesh = m_indexedMeshes[subpart]; numverts = mesh.m_numVertices; vertexbase = mesh.m_vertexBase; type = mesh.m_vertexType; vertexStride = mesh.m_vertexStride; numfaces = mesh.m_numTriangles; indexbase = mesh.m_triangleIndexBase; indexstride = mesh.m_triangleIndexStride; indicestype = mesh.m_indexType; }
// creates a random, fractal heightfield public void SetFractal(byte[] grid,int gridIndex,int bytesPerElement,PHY_ScalarType type,int step) { Debug.Assert(grid != null); Debug.Assert(bytesPerElement > 0); Debug.Assert(step > 0); Debug.Assert(step < s_gridSize); int newStep = step / 2; // std::cerr << "Computing grid with step = " << step << ": before\n"; // dumpGrid(grid, bytesPerElement, type, step + 1); // special case: starting (must set four corners) if (s_gridSize - 1 == step) { // pick a non-zero (possibly negative) base elevation for testing float baseValue = RandomHeight(step / 2); ConvertFromFloat(grid ,gridIndex, baseValue, type); ConvertFromFloat(grid ,gridIndex + step * bytesPerElement, baseValue, type); ConvertFromFloat(grid ,gridIndex + step * s_gridSize * bytesPerElement, baseValue, type); ConvertFromFloat(grid ,gridIndex + (step * s_gridSize + step) * bytesPerElement, baseValue, type); } // determine elevation of each corner float c00 = ConvertToFloat(grid,gridIndex, type); float c01 = ConvertToFloat(grid, gridIndex + step * bytesPerElement, type); float c10 = ConvertToFloat(grid, gridIndex + (step * s_gridSize) * bytesPerElement, type); float c11 = ConvertToFloat(grid, gridIndex + (step * s_gridSize + step) * bytesPerElement, type); // set top middle UpdateHeight(grid, gridIndex + newStep * bytesPerElement, 0.5f * (c00 + c01) + RandomHeight(step), type); // set left middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize) * bytesPerElement, 0.5f * (c00 + c10) + RandomHeight(step), type); // set right middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize + step) * bytesPerElement, 0.5f * (c01 + c11) + RandomHeight(step), type); // set bottom middle UpdateHeight(grid, gridIndex + (step * s_gridSize + newStep) * bytesPerElement, 0.5f * (c10 + c11) + RandomHeight(step), type); // set middle UpdateHeight(grid, gridIndex + (newStep * s_gridSize + newStep) * bytesPerElement, 0.25f * (c00 + c01 + c10 + c11) + RandomHeight(step), type); // std::cerr << "Computing grid with step = " << step << ": after\n"; // dumpGrid(grid, bytesPerElement, type, step + 1); // terminate? if (newStep < 2) { return; } // recurse SetFractal(grid,gridIndex, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + newStep * bytesPerElement, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + (newStep * s_gridSize) * bytesPerElement, bytesPerElement, type, newStep); SetFractal(grid, gridIndex + ((newStep * s_gridSize) + newStep) * bytesPerElement, bytesPerElement, type, newStep); }
/// protected initialization /** Handles the work of constructors so that public constructors can be backwards-compatible without a lot of copy/paste. */ protected void Initialize(int heightStickWidth, int heightStickLength, byte[] heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, PHY_ScalarType hdt, bool flipQuadEdges) { // validation Debug.Assert(heightStickWidth > 1, "bad width"); Debug.Assert(heightStickLength > 1, "bad length"); Debug.Assert(heightfieldData != null, "null heightfield data"); // Debug.Assert(heightScale) -- do we care? Trust caller here Debug.Assert(minHeight <= maxHeight, "bad min/max height"); Debug.Assert(upAxis >= 0 && upAxis < 3, "bad upAxis--should be in range [0,2]"); Debug.Assert(hdt != PHY_ScalarType.PHY_UCHAR || hdt != PHY_ScalarType.PHY_FLOAT || hdt != PHY_ScalarType.PHY_SHORT, "Bad height data type enum"); // initialize member variables m_shapeType = BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE; m_heightStickWidth = heightStickWidth; m_heightStickLength = heightStickLength; m_minHeight = minHeight; m_maxHeight = maxHeight; m_width = (heightStickWidth - 1); m_length = (heightStickLength - 1); m_heightScale = heightScale; m_heightFieldData = heightfieldData; m_heightDataType = hdt; m_flipQuadEdges = flipQuadEdges; m_useDiamondSubdivision = false; m_upAxis = upAxis; m_localScaling = new Vector3(1f, 1f, 1f); // determine min/max axis-aligned bounding box (aabb) values switch (m_upAxis) { case 0: { m_localAabbMin = new Vector3(m_minHeight, 0, 0); m_localAabbMax = new Vector3(m_maxHeight, m_width, m_length); break; } case 1: { m_localAabbMin = new Vector3(0, m_minHeight, 0); m_localAabbMax = new Vector3(m_width, m_maxHeight, m_length); break; }; case 2: { m_localAabbMin = new Vector3(0, 0, m_minHeight); m_localAabbMax = new Vector3(m_width, m_length, m_maxHeight); break; } default: { //need to get valid m_upAxis Debug.Assert(false, "Bad m_upAxis"); break; } } // remember origin (defined as exact middle of aabb) m_localOrigin = 0.5f * (m_localAabbMin + m_localAabbMax); }
public void DumpGrid(byte[] grid,int bytesPerElement,PHY_ScalarType type,int max) { //std::cerr << "Grid:\n"; for (int j = 0; j < max; ++j) { for (int i = 0; i < max; ++i) { long offset = j * s_gridSize + i; float z = ConvertToFloat(grid, (int)(offset * bytesPerElement), type); //sprintf(buffer, "%6.2f", z); //std::cerr << " " << buffer; } //std::cerr << "\n"; } }
/// preferred constructor /** This constructor supports a range of heightfield data types, and allows for a non-zero minimum height value. heightScale is needed for any integer-based heightfield data types. */ public HeightfieldTerrainShape(int heightStickWidth, int heightStickLength, byte[] heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges) { Initialize(heightStickWidth, heightStickLength, heightfieldData, heightScale, minHeight, maxHeight, upAxis, heightDataType, flipQuadEdges); }
public void AddIndexedMesh(IndexedMesh mesh, PHY_ScalarType indexType) { m_indexedMeshes.Add(mesh); m_indexedMeshes[m_indexedMeshes.Count-1].m_indexType = indexType; }
public byte[] GetRawHeightfieldData(eTerrainModel model, PHY_ScalarType type, ref float minHeight, ref float maxHeight) { // std::cerr << "\nRegenerating terrain\n"; // std::cerr << " model = " << model << "\n"; // std::cerr << " type = " << type << "\n"; long nElements = ((long)s_gridSize) * s_gridSize; // std::cerr << " nElements = " << nElements << "\n"; int bytesPerElement = GetByteSize(type); // std::cerr << " bytesPerElement = " << bytesPerElement << "\n"; Debug.Assert(bytesPerElement > 0, "bad bytes per element"); long nBytes = nElements * bytesPerElement; // std::cerr << " nBytes = " << nBytes << "\n"; byte[] raw = new byte[nBytes]; Debug.Assert(raw != null, "out of memory"); // reseed randomization every 30 seconds // srand(time(NULL) / 30); // populate based on model switch (model) { case eTerrainModel.eRadial: SetRadial(raw, bytesPerElement, type); break; case eTerrainModel.eFractal: for (int i = 0; i < nBytes; i++) { raw[i] = 0; } SetFractal(raw, 0, bytesPerElement, type, s_gridSize - 1); break; default: Debug.Assert(false, "bad model type"); break; } if (false) { // inside if(0) so it keeps compiling but isn't // exercised and doesn't cause warnings // std::cerr << "final grid:\n"; DumpGrid(raw, bytesPerElement, type, s_gridSize - 1); } // find min/max for (int i = 0; i < s_gridSize; ++i) { for (int j = 0; j < s_gridSize; ++j) { float z = GetGridHeight(raw, i, j, type); // std::cerr << "i=" << i << ", j=" << j << ": z=" << z << "\n"; // update min/max if (i == 0 && j == 0) { minHeight = z; maxHeight = z; } else { if (z < minHeight) { minHeight = z; } if (z > maxHeight) { maxHeight = z; } } } } if (maxHeight < -minHeight) { maxHeight = -minHeight; } if (minHeight > -maxHeight) { minHeight = -maxHeight; } // std::cerr << " minHeight = " << minHeight << "\n"; // std::cerr << " maxHeight = " << maxHeight << "\n"; return(raw); }