September 04, 2007 [Issue 1190] Reference becoming null | ||||
---|---|---|---|---|
| ||||
Posted in reply to d-bugmail | http://d.puremagic.com/issues/show_bug.cgi?id=1190 ------- Comment #9 from wbaxter@gmail.com 2007-09-04 11:46 ------- Created an attachment (id=175) --> (http://d.puremagic.com/issues/attachment.cgi?id=175&action=view) Simpler test case -- |
September 05, 2007 [Issue 1190] Reference becoming null | ||||
---|---|---|---|---|
| ||||
Posted in reply to d-bugmail | http://d.puremagic.com/issues/show_bug.cgi?id=1190 davidl@126.com changed: What |Removed |Added ---------------------------------------------------------------------------- Status|REOPENED |RESOLVED Resolution| |INVALID ------- Comment #10 from davidl@126.com 2007-09-05 03:06 ------- The Simpler test case just should access violation as Bill said in his last comment. Kill the ref keyword, the simpler test case goes fine. But without inout the original code fails at an assertion. And I'm kind enough to illustrate the new problem for you. :) Here is the code with a little bit correction, and illustrate the new assertion fail reason: import std.string; import std.stream; import std.math; alias std.string.toString strfy; alias std.stdio.writefln Log; int staticc; private static const ubyte PatchSize=64; //must be power of 2 //const ubyte MAX_DEPTH=cast(ubyte)(2*sqrt(PatchSize)+1); private static const MaxDepth=4;//17 private struct CamInfo { uint x, y, z; } private CamInfo camInfo; /** The Terrain engine class. */ public final class Terrain { ///The TreeNode class is used to store information about the patch tree private final class TreeNode { private TreeNode lChild, rChild, bNeigh, lNeigh, rNeigh; public this() { reset(); } public void reset() { lChild=rChild=bNeigh=lNeigh=rNeigh=null; } } ///Patch Class. A terrain is composed of several patches private final class Patch { this (ushort x, ushort y) { this.x=x; this.y=y; visible=true; rNode=new TreeNode(); //these never get reassigned lNode=new TreeNode(); //they're const, remmember? assert(rNode !is null); assert(lNode !is null); //nodes come already clean rNode.bNeigh=lNode; lNode.bNeigh=rNode; calcError(); } ///Resets the patch. It becomes undivided and ready for the next tesselation. void reset() { rNode.reset(); lNode.reset(); rNode.bNeigh=lNode; lNode.bNeigh=rNode; //TODO: check visibility here visible=true; } ///Tesselates this patch and it becomes ready for rendering void update() { std.gc.disable(); cError[]=lError; tesselate(lNode, x, cast(ushort)(y+PatchSize), cast(ushort)(x+PatchSize), y, x, y, 1); cError[]=rError; tesselate(rNode, cast(ushort)(x+PatchSize), y, x, cast(ushort)(y+PatchSize), cast(ushort)(x+PatchSize), cast(ushort)(y+PatchSize), 1); std.gc.enable(); } ///This patch's error measure is updated from the data on hData. void calcError() { Calculate(x, cast(ushort)(y+PatchSize), cast(ushort)(x+PatchSize), y, x, y, 1); lError[]=cError; //copy the error data Calculate(cast(ushort)(x+PatchSize), y, x, cast(ushort)(y+PatchSize), cast(ushort)(x+PatchSize), cast(ushort)(y+PatchSize), 1); rError[]=cError; return; } ~this() { delete rNode; delete lNode; } //no need to protect this vars because this class won't be accessed from the exterior const ushort x, y; //coordinates aren't expected to change bool visible; ///Result of lastest cull check ushort[MaxDepth] lError, rError; const TreeNode rNode, lNode; } public: this(in File file, float detail) in { assert(file !is null); assert(detail>=0); //negative error values make no sense. 0 values are purely debugging technices } body { debug Log("Creating terrain object"); this.detail=detail; file.read(sizeX); file.read(sizeY); assert((sizeX>0)&&(sizeY>0)); HMult=1; //TODO: Replace with resolution from file hData=new ushort[][](sizeX, sizeY); //initialize data debug Log(" * Loading terrain data..."); foreach (stripe; hData) foreach (inout vertex; stripe) { file.read(vertex); vertex*=HMult; } maxH=0; //Maximum height present on the map. Used somewhere in debug code foreach (stripe; hData) foreach (vertex; stripe) if (vertex>maxH) maxH=vertex; xPatch=cast(ubyte)(sizeX/PatchSize); yPatch=cast(ubyte)(sizeY/PatchSize); nodes.length=(sizeX*sizeY/PatchSize); debug Log(" * Creating terrain nodes ("~strfy(nodes.length)~")..."); foreach(inout node; nodes) node=new TreeNode; debug Log(" * Creating patches..."); patches.length=xPatch;//how many columns will we have? Patch[col][row] foreach (size_t row, inout Patch[] strip; patches) { assert(strip.length==0, "Oups! Redefining a length of a strip"); strip.length=yPatch; //How many rows? foreach (size_t col, inout Patch patch; strip) patch=new Patch(cast(ushort)(col*PatchSize), cast(ushort)(row*PatchSize)); } debug Log("Terrain creation complete"); } public float heightAt(in float x, in float y) out(result) { assert((result>=0.0f)||(result==float.nan)); } body { if ((x<0.0)||(y<0.0f)) return float.nan; //triangulate height at the given point return 0.0f; } ///Makes the terrain ready for rendering. Tesselates all the patches. void update() body { //reset the engine first currNode=0; //reset current node foreach (size_t row, inout Patch[] strip; patches) foreach (size_t col, inout Patch patch; strip) { patch.reset(); //Recalculate the visibility flag if (patch.visible) //bail on non-visible patches { if (col>0) patch.lNode.lNeigh=patches[col-1][row].rNode; else patch.lNode.lNeigh=null; if (col<(xPatch-1)) patch.rNode.lNeigh=patches[col+1][row].lNode; else patch.rNode.lNeigh=null; if (row>0) patch.lNode.rNeigh=patches[col][row-1].rNode; else patch.lNode.rNeigh=null; if (row<(yPatch-1)) patch.rNode.rNeigh=patches[col][row+1].lNode; else patch.rNode.rNeigh=null; } } //Unfloatize camera information. Remember the case in which the coords are <0 foreach (Patch[] strip; patches) foreach (Patch patch; strip) if (patch.visible) patch.update(); return; } ~this() { /*Since the terrain is the last thing to be deleted when a game is over, a gc.fullCollect() will be preformed sometime soon, so it is not necessary to clean up manually*/ foreach (inout stripe; hData) { stripe.length=0; delete stripe; } hData.length=0; foreach (inout Patch[] strip; patches) foreach (inout Patch patch; strip) delete patch; foreach(inout node; nodes) delete node; nodes.length=0; } private: ///Returns the next available TreeNode. Creates a new one if there are no more available. TreeNode newNode() out(result) { assert(result !is null, "newNode() attempted to return a null TreeNode."); } body { assert(currNode<=nodes.length, "currNode advanced too much"); if (currNode==nodes.length) { nodes~=new TreeNode; //no more nodes available, create another one debug Log("Insufficient TreeNodes for current tesselation (detail="~strfy(detail)~"). Increasing TreeNode pool (currNode="~strfy(currNode)~")."); } TreeNode ret=nodes[currNode]; //get a new node ret.reset(); //clear new node's affiliations currNode++; return ret; } ///To be used by functions of the class, returns raw height value. ushort heightAt(ushort x, ushort y) { if ((x==sizeX)||(y==sizeY)) return 0; //Out of bounds values are not drawn (taken has void in game) assert(x<sizeX, "Out of bounds value requested"); assert(y<sizeY, "Out of bounds value requested"); return hData[x][y]; } ushort Calculate(ushort lX, ushort lY, ushort rX, ushort rY, ushort aX, ushort aY, ushort node) { //Calculate hipotenuse ushort cX=cast(ushort)((rX+lX)/2); ushort cY=cast(ushort)((rY+lY)/2); ushort cZ=heightAt(cX, cY); ushort rZ=heightAt(rX, rY); ushort lZ=heightAt(lX, lY); ushort errRatio=cast(ushort)abs(cZ-((lZ+rZ)/2)); //this iteraction's primary error ratio if (errRatio==0) return 0; //reached perfectness if ((abs((rX-lX))>=2)||(abs(rY-lY))>=2) //smaller than 2x2 { //calculate the error of one of the child triangles ushort tempErrRatio=Calculate(aX, aY, lX, lY, cX, cY, cast(ushort)(node*2)); if (errRatio<tempErrRatio) errRatio=tempErrRatio; //error is greater, adopt it //and then try the other child tempErrRatio=Calculate(rX, rY, aX, aY, cX, cY, cast(ushort)(node*2+1)); if (errRatio<tempErrRatio) errRatio=tempErrRatio; //error is greater, adopt it } //TODO: this "if" may not need to be here: required for range check, nothing else if (node<MaxDepth) cError[node]=errRatio; //store error ratio as we descend on the tree return errRatio; //return this levels error ratio } ///Splits a TreeNode, used by Patch sub-class void split(TreeNode myNode) in { if (myNode.rNeigh !is null) // assert(myNode.rNeigh.bNeigh !is myNode); assert(myNode !is null, "Attempted to split a null node"); } body { if (myNode.lChild !is null) return; //already split //Check to see if current triangle is diamond with bNeigh if ((myNode.bNeigh !is null)&&(myNode.bNeigh.bNeigh !is myNode)) { TreeNode v=myNode.bNeigh; split(myNode.bNeigh); //it has to be split, so that current triangle can be a diamond with it's bNeigh if (myNode.bNeigh !is v){ printf("oh my god!"); } assert(myNode.bNeigh.lChild !is null, "Split failed!"); } //asserts that I am a diamond with my bNeigh, or I'm a border node assert ((myNode.bNeigh is null)||(myNode.bNeigh.bNeigh is myNode), "Attempted to split a node that is not part of a diamond"); myNode.lChild=newNode(); myNode.rChild=newNode(); //with (myNode) //in this block, all the relations of myNode are updated { myNode.lChild.bNeigh=myNode.lNeigh; myNode.lChild.lNeigh=myNode.rChild; myNode.rChild.bNeigh=myNode.rNeigh; myNode.rChild.rNeigh=myNode.lChild; if (myNode.lNeigh !is null) //I have a left neigh { if (myNode.lNeigh.bNeigh is myNode) myNode.lNeigh.bNeigh=myNode.lChild; else { if (myNode.lNeigh.lNeigh is myNode) myNode.lNeigh.lNeigh=myNode.lChild; else if (myNode.lNeigh.rNeigh is myNode) myNode.lNeigh.rNeigh=myNode.lChild; } } assert((myNode.lChild !is null)&&(myNode.rChild !is null), "Children died at lNeigh update"); //The following if block causes the children to become null if (myNode.rNeigh !is null) //I have a right neigh { if (myNode.rNeigh.bNeigh is myNode) { assert(myNode.lChild !is null); assert(myNode.rChild !is null); assert(myNode.rNeigh.bNeigh !is null); // assert(myNode.rNeigh.bNeigh !is myNode); myNode.rNeigh.bNeigh=myNode.rChild; //This causes the children (both) to die assert(myNode.lChild !is myNode.rNeigh.bNeigh); assert(myNode.lChild !is null); //crash here assert(myNode.rChild !is null); } else { if (myNode.rNeigh.rNeigh is myNode) myNode.rNeigh.rNeigh=myNode.rChild; else if (myNode.rNeigh.lNeigh is myNode) myNode.rNeigh.lNeigh=myNode.rChild; } } assert((myNode.lChild !is null)&&(myNode.rChild !is null), "Children died at rNeigh update"); if (myNode.bNeigh !is null) //I have a base neigh { if (myNode.bNeigh.lChild !is null) //My bNeigh is already split { assert(myNode.bNeigh.rChild !is null, "bNeigh is only partially split, cannot update relations."); myNode.bNeigh.lChild.rNeigh=myNode.rChild; myNode.bNeigh.rChild.lNeigh=myNode.lChild; myNode.lChild.rNeigh=myNode.bNeigh.rChild; myNode.rChild.lNeigh=myNode.bNeigh.lChild; } else split(myNode.bNeigh); //Split my base. He'll take care of Neigh relations } else //I don't have bNeigh { myNode.lChild.rNeigh=null; myNode.rChild.lNeigh=null; } assert((myNode.lChild !is null)&&(myNode.rChild !is null), "Children died at bNeigh update"); } //updates to myNode are over assert((myNode.lChild !is null)&&(myNode.rChild !is null), "Node children have been lost!"); // this assertion doesn't test the problem actually , in this scope testing myNode is useless // you should test the copy of myNode return; } void tesselate (TreeNode myNode, ushort lX, ushort lY, ushort rX, ushort rY, ushort aX, ushort aY, ushort node) in { if (myNode.rNeigh !is null) assert(myNode.rNeigh.bNeigh !is myNode); assert(myNode !is null, "Attempted to tesselate a null node"); } body { //Calculate hipotenuse ushort cX=cast(ushort)((rX+lX)/2); ushort cY=cast(ushort)((rY+lY)/2); ushort cZ=heightAt(cX, cY); ulong distance=(cX-camInfo.x)*(cX-camInfo.x)+(cY-camInfo.y)*(cY-camInfo.y)+(cZ-camInfo.z)*(cZ-camInfo.z); ulong threshold=cError[node]*cError[node]; if ((threshold*detail)>distance) //should this triangle be split? { split(myNode); //won't fail because TreeNodes will not go out //I have children and they aren't too small, //TODO: Unnecessary check: split never fails: (myNode.lChild !is null)&&(myNode.rChild !is null)&& if ((node<cast(ushort)(MaxDepth/2))&&((abs(lX-rX)>=2)||(abs(lY-rY)>=2))) { tesselate(myNode.lChild, aX, aY, lX, lY, cX, cY, cast(ushort)(node*2)); tesselate(myNode.rChild, rX, rY, aX, aY, cX, cY, cast(ushort)(node*2+1)); } } } const ushort maxH; float detail; ///Detail Threshold TreeNode nodes[]; ///Node pool size_t currNode; ///Current node being assigned const ushort sizeX, sizeY; const ubyte xPatch, yPatch; ushort hData[][]; ///The Height Data static ushort[MaxDepth] cError; //error buffer const ubyte HMult; ///Height multiplier, determines the resolution of the terrain const Patch patches[][]; ///The pathes that compose the terrain. } void main() { File este=new File("novomapa.raw", FileMode.In); Terrain terrain=new Terrain(este, 2500); delete este; std.stdio.writefln("Preparing for rendering..."); terrain.update(); delete terrain; } -- |
September 05, 2007 [Issue 1190] Reference becoming null | ||||
---|---|---|---|---|
| ||||
Posted in reply to d-bugmail | http://d.puremagic.com/issues/show_bug.cgi?id=1190 ------- Comment #11 from wbaxter@gmail.com 2007-09-05 03:26 ------- If you're going to add gigantic gobs of code to bugzilla, please do it as an attachment so that the lines don't get all wrapped. -- |
Copyright © 1999-2021 by the D Language Foundation