//Used by the two constructors. protected void _fixConvex() { // I rewrote this to make it efficient for a linked list // First, test a bunch using a foreach -- JC bool first = true; bool second = true; cVector3 v1 = new cVector3(); cVector3 v2 = new cVector3(); float test; foreach (cVector3 v3 in _vectorvert) { if (!first && !second) { test = ((v2.sub(v1)).mult((v3.sub(v1)))).Z; /* Take the cross product and look at the z component. * If it is positive, the cross product points up, so this is * counterclockwise rotation and you aren't convex. */ if (test < 0) { _convex = false; break; } v1.copy(v2); v2.copy(v3); } else if (first) { v1.copy(v3); first = false; } else // second { v2.copy(v3); second = false; } } // finally, do two special tests -- JC cVector3 last = _vectorvert[0]; test = ((v2.sub(v1)).mult((last.sub(v1)))).Z; if (test < 0) { _convex = false; return; } v1.copy(v2); v2.copy(last); last = _vectorvert[1]; test = ((v2.sub(v1)).mult((last.sub(v1)))).Z; if (test < 0) { _convex = false; } }
public override int clamp(cRealBox3 border) { /* We don't change _pskeleton as it has the geometric info. We just change _position. */ if (_baseAccessControl == 1) return base.clamp(border); cRealBox3 effectivebox = border; cVector3 oldcorner; cVector3 newcorner = new cVector3(); int outcode = 0; int totaloutcode = 0; for (int i = 0; i < 8; i++) //Step through the wall's corners { oldcorner = _pskeleton.corner(i).add(_position); newcorner.copy(oldcorner); outcode = effectivebox.clamp(newcorner); if (outcode != cRealBox3.BOX_INSIDE) //corner was moved { _position.addassign(newcorner.sub(oldcorner)); /* As long at the wall is small enough to fit inside the border, the successive corrections won't cancel each other out. */ totaloutcode |= outcode; } } _wrapposition1.copy(_position); _wrapposition2.copy(_position); _wrapposition3.copy(_position); /* So it won't think it wrapped. */ return outcode; }
public override cVector3 force(cCritter pcritter) { float area = pcritter.Radius * pcritter.Radius; float mass = pcritter.Mass; return(_windvector.sub(pcritter.Velocity).mult(area * _intensity)); }
//Accessors //Mutators /// <summary> /// Aims the weapon at the parameter. If the AimToAttitudeLock property has been set to true, /// it also sets the Tangent of the shooter to what it is aiming at. /// </summary> /// <param name="vclick">The point to aim at.</param> public virtual void aimAt(cVector3 vclick) { if (!_armed) { return; } AimVector = (vclick.sub(_position)).Direction; /* Use the setAimVector call as this * does a safety roundOff on the direction. */ if (_aimtoattitudelock) { AttitudeTangent = _aimvector; } }
public virtual cLine pixelToSightLine(int xpix, int ypix) //This is virtual, cGraphicsMFC does different { /* This is a line that runs from the viewer's eye to the direction matching the pixel point. */ cVector3 nearpoint = pixelToVector(xpix, ypix, 0.0f); cVector3 farpoint = pixelToVector(xpix, ypix, 1.0f); cVector3 tangent = farpoint.sub(nearpoint); if (tangent.IsPracticallyZero) { tangent = new cVector3(0.0f, 0.0f, -1.0f); } else { tangent.normalize(); } return(new cLine(nearpoint, tangent)); //Second arg should be a unit vector. }
//Mutators public void setEndsThicknessHeight(cVector3 enda, cVector3 endb, float thickness = THICKNESS, float height = WALLPRISMDZ) { _position = enda.add(endb).mult(0.5f); _wrapposition1.copy(_position); _wrapposition2.copy(_position); _wrapposition3.copy(_position); /* This line is important, as otherwise the cCritter.draw will thing this thing was wrapped, and it'll get drawn in two places. */ _tangent = endb.sub(enda); float length = _tangent.Magnitude; _tangent.normalize(); _oldtangent.copy(_tangent); _normal = _tangent.defaultNormal(); /* We orient so that the normal is oriented to the tangent as the "y-axis" is to the the "x-axis".*/ _binormal = _tangent.mult(_normal); _attitude = new cMatrix3(_tangent, _normal, _binormal, _position); Skeleton = new cRealBox3(length, thickness, height); Speed = 0.0f; /* Also sets _velocity to ZEROVECTOR, but doesn't wipe out _direction. */ /*In looking at these settings, think of the wall as aligned horizontally with endb - enda pointing to the right and the normal pointing into the screen*/ cPolygon ppolygon = new cPolygon(4); ppolygon.Edged = true; ppolygon.FillColor = Color.Gray; ppolygon.LineWidthWeight = cColorStyle.LW_IGNORELINEWIDTHWEIGHT; ppolygon.LineWidth = 1; //Means draw a one-pixel edge line. ppolygon.setVertex(0, new cVector3(0.5f * length, 0.5f * thickness)); ppolygon.setVertex(1, new cVector3(-0.5f * length, 0.5f * thickness)); ppolygon.setVertex(2, new cVector3(-0.5f * length, -0.5f * thickness)); ppolygon.setVertex(3, new cVector3(0.5f * length, -0.5f * thickness)); ppolygon.fixCenterAndRadius(); /* Use this call after a bunch of setVertex if points are just where you want. */ ppolygon.SpriteAttitude = cMatrix3.translation(new cVector3(0.0f, 0.0f, -height / 2.0f)); /* This corrects for the fact that we always draw the ppolygon with its bottom face in the xy plane and its top in the plane z = height. We shift it down so it's drawn to match the skeleton positon. */ Sprite = ppolygon; /* Also sets cSprite._prismdz to cCritter._defaultprismdz, which we set to CritterWall.WALLPRISMDZ in our cCritterWall constructor. */ }
/* Set the perspective matrix of the pgraphics * of the _pownerview so that something like _proportionofworldtoshow much of the world * shows (0.1 would be a tenth of the world, 1.0 would be all of it, * 2.0 would mean show space aroudn the world so it takes up half the * view. Not const as stereo viewer flips position temporarily. */ /*This tries to set _znear and _zfar * so that each of the eight corners of the worldbox is between * the planes cutting the attitudeTangent() direction at distances of _znear and _zfar from the * position(). We don't take into account the corners that are behind the viewer. If * any point is behind or almost behind the viewer pos relative to the * viewedirection we set _znear to cCritterViewer::MINZNEAR, which is typically 0.1. */ public bool isVisible(cVector3 testpos) { /* _foveaproportion lies between 0.0 and 1.0. We say something is visible if it * appears on the inner foveaproportion central box of the viewer's image screen, * which is what you see in the view window. */ if (!_perspective) { cRealBox2 fovea; fovea = OrthoViewRect; fovea = fovea.innerBox((1.0f - _foveaproportion) * fovea.MinSize); return(fovea.inside(new cVector2(testpos).sub(new cVector2(_position)))); } else { cVector3 totestpos = (testpos.sub(Position)).normalize(); return(Math.Abs(totestpos.angleBetween(AttitudeTangent)) < _foveaproportion * 0.5f * _fieldofviewangle); } }
/* Overload this so as not to change velocity as I normally want my walls to be stable and not drift after being dragged. */ public override bool collide(cCritter pcritter) { cVector3 oldlocalpos, newlocalpos; float newdistance; int oldoutcode, newoutcode; bool crossedwall; oldlocalpos = globalToLocalPosition(pcritter.OldPosition); oldoutcode = _pskeleton.outcode(oldlocalpos); newlocalpos = globalToLocalPosition(pcritter.Position); newdistance = _pskeleton.distanceToOutcode(newlocalpos, out newoutcode); //Sets the newoutcode as well. crossedwall = crossed(oldoutcode, newoutcode); if (newdistance >= pcritter.Radius && !crossedwall) //No collision return false; /*See if there's a collision at all. We say there's a collision if crossedwall or if the cCritterWall.distance is less than radius. Remember that cCritterWall.distance measures the distance to the OUTSIDE PERIMETER of the box, not the distance to the box's center. */ /* I collided, so I need to move back further into the last good zone I was in outside the wall. I want to set newlocalpos so the rim of its critter is touching the wall. The idea is to back up in the direction of oldlocalpos. To allow the possibility of skidding along the wall, we plan to back up from the the face (or edge or corner) facing oldlocalpos. This works only if oldlocalpos was a good one, not inside the box. In principle this should always be true, but some rare weird circumstance (like a triple collsion) might mess this up, so we check for the bad case before starting. */ if (oldoutcode == cRealBox3.BOX_INSIDE) //Note that this almost never happens. { cVector3 insidepos = new cVector3(); insidepos.copy(oldlocalpos); oldlocalpos.subassign(pcritter.Tangent.mult(_pskeleton.MaxSize)); //Do a brutally large backup to get out of the box for sure. oldoutcode = _pskeleton.outcode(oldlocalpos); //Recalculate outcode at this new position. oldlocalpos = _pskeleton.closestSurfacePoint(oldlocalpos, oldoutcode, insidepos, cRealBox3.BOX_INSIDE, false); //Go to the closest surface point from there. oldoutcode = _pskeleton.outcode(oldlocalpos); //Recalculate outcode one more time to be safe. crossedwall = crossed(oldoutcode, newoutcode); //Recalculate crossedwall } /* I find that with this code, the mouse can drag things through walls, so I do a kludge to block it by setting crossedwall to TRUE, this affects the action of cRealBox.closestSurfacePoint, as modified in build 34_4. */ if (pcritter.Listener.IsKindOf("cListenerCursor")) crossedwall = true; //Don't trust the mouse listener. newlocalpos = _pskeleton.closestSurfacePoint(oldlocalpos, oldoutcode, newlocalpos, newoutcode, crossedwall); /* This call to closestSurfacePoint will move the newlocal pos from the far new side (or inside, or overlapping) of the box back to the surface, usually on the old near side, edge, or corner given by oldoutcode. This prevents going through the wall. If oldoutcode is a corner position and you are in fact heading towards a face near the corner, we used to bounce off the corner even though visually you can see you should bounce off the face. This had the effect of making a scooter player get hung up on a corner sometimes. As of build 34_3, I'm moving the newlocalpos to the newoutocode side in the case where oldlocalpos is an edge or a corner, and where crossedwall isn't TRUE. I have to force in a TRUE for the cCursorLIstener case. The USEJIGGLE code below also helps keep non-player critters from getting stuck on corners. */ //Now back away from the box. newoutcode = _pskeleton.outcode(newlocalpos); cVector3 avoidbox = _pskeleton.escapeVector(newlocalpos, newoutcode); newlocalpos.addassign(avoidbox.mult(pcritter.Radius)); newoutcode = _pskeleton.outcode(newlocalpos); pcritter.moveTo(localToGlobalPosition(newlocalpos), true); //TRUE means continuous motion, means adjust tangent etc. //Done with position, now change the velocity cVector3 localvelocity = globalToLocalDirection(pcritter.Velocity); cVector3 oldlocalvelocity = new cVector3(); oldlocalvelocity.copy(localvelocity); _pskeleton.reflect(localvelocity, newoutcode); /* I rewrote the reflect code on Feb 22, 2004 for VErsion 34_3, changing it so that when you reflect off an edge or corner, you only bounce the smallest of your three velocity components. Balls stll seem to get hung up on the corner once is awhile. */ /* Now decide, depending on the pcritter's absorberflag and bounciness, how much you want to use the new localvelocity vs. the oldlocalvelocity. We decompose the new localvelocity into the tangentvelocity parallel to the wall and the normalvelocity away from the wall. Some pencil and paper drawings convince me that the tangent is half the sum of the oldlocalvelocity and the reflected new localvelocity. */ cVector3 tangentvelocity = localvelocity.add(oldlocalvelocity).mult(0.5f); cVector3 normalvelocity = localvelocity.sub(tangentvelocity); float bouncefactor = 1.0f; if (pcritter.AbsorberFlag) bouncefactor = 0.0f; else bouncefactor = pcritter.Bounciness; localvelocity = tangentvelocity.add(normalvelocity.mult(bouncefactor)); /* Maybe the rotation should depend on the kind of edge or corner. Right now let's just use critter's binormal. Don't to it to the player or viewer as it's confusing. */ if (!(cRealBox3.isFaceOutcode(newoutcode)) && //edge or corner !(pcritter.IsKindOf("cCritterViewer")) && //not viewer !(pcritter.IsKindOf("cCritterArmedPlayer"))) //Not player. Note that cPlayer inherits from cCritterArmedPlayer, //so don't use cCritterPlayer as the base class here. { localvelocity.rotate(new cSpin( Framework.randomOb.randomReal( -cCritterWall.CORNERJIGGLETURN, cCritterWall.CORNERJIGGLETURN), //A random turn pcritter.Binormal)); //Around the critter's binormal localvelocity.multassign(cCritterWall.CORNERJIGGLEKICK); //Goose it a little } pcritter.Velocity = localToGlobalDirection(localvelocity); return true; }