/// <summary> /// Clone this cell /// </summary> /// <param name="gene">The gene specifying this cell instance's orientation, wiring, etc.</param> /// <param name="owner">The organism that owns me</param> private Cell Clone(Gene gene, Organism owner) { // Make a shallow copy of all members Cell newCell = (Cell)this.MemberwiseClone(); // Replace members that are CLONED copies of objects in the master... newCell.rootFrame = JointFrame.CloneHierarchy(rootFrame); // my unique copy of the frame hierarchy // TODO: Clone any other members here // Replace or create members that are UNIQUE to this instance... newCell.instance = instance++; // my instance number (tally in master) newCell.PlugOrientation = gene.Orientation; // my orientation in the parent socket newCell.owner = owner; // my owning organism newCell.joint = JointFrame.IndexFrameType( newCell.rootFrame,JointFrame.FrameType.Animating); // lists of my animation, hotspot & skt frames newCell.hotspot = JointFrame.IndexFrameType( newCell.rootFrame, JointFrame.FrameType.Hotspot); newCell.socket = JointFrame.IndexFrameType( newCell.rootFrame, JointFrame.FrameType.Socket); newCell.physiology = Physiology.LoadPhysiology(newCell.group, newCell.name, newCell.variantName, // my Physiology class containing functionality newCell, newCell.joint.Length, newCell.socket.Length); newCell.GetMyVariantIndex(); // Convert variant name into an index before trying to access channel data newCell.channel = newCell.LoadChannels(gene); // lists of channel info from gene AND mesh frames newCell.collisionFrames = new List<JointFrame>(); newCell.GetMeshFrames(newCell.rootFrame, newCell.collisionFrames, false); // a list of mesh-containing frames for collision tests // Set all the internal (organelle) frames to invisible, because they aren't normally rendered newCell.HideOrganelles(); // TODO: Initialise other instance-specific members here // Now that everything is set up, call the Physiology subclass's Init() method to allow the physiology to initialise itself newCell.physiology.Init(); // Add a device reset handler to rebuild this cell's resources Engine.Device.DeviceReset += new System.EventHandler(newCell.OnReset); return newCell; }
/// <summary> /// Get a clone of this Cell from the library, loading it first if necessary /// </summary> /// <returns>A modifiable clone of the named Cell</returns> /// <param name="gene">The gene containing the cell's properties, orientation and wiring</param> /// <param name="owner">The organism that owns me</param> public static Cell Get(Gene gene, Organism owner) { Cell cell = null; // Get name of the cell from the gene string fullname = gene.XFile(); // if the library already contains a Cell of that name, that's what we'll clone from if (library.ContainsKey(fullname)) { cell = (Cell)library[fullname]; // Debug.WriteLine(" Cell: Created new Cell instance: "+name+":"+cell.instance); } // otherwise, create a new entry in the library by loading a whole X file else { cell = new Cell(fullname); // Debug.WriteLine(" Cell: Created new Cell archetype: "+name+":"+cell.instance); } // Return a cloned instance of this Cell Cell newCell = cell.Clone(gene, owner); newCell.CreateBoundMarker(); // temp: create marker to show bounding sphere return newCell; }
/// <summary> /// Update (called whether this is the current cameraship or not) /// </summary> public override void Update() { /// HACK: FOR TESTING ONLY - ROTATE THE SHIP ///RotateBy(0.03f, 0.05f, 0.02f); // Steer the spotlight to follow the camera if (CurrentShip==this) Fx.SetSpotlightPosition(Matrix.Invert(rootCell.GetHotspotNormalMatrix(0)) * Camera.ProjMatrix); // If there is a creature on the clamp, position/orient it so that it sticks to the clamp (hotspot 1) if (SelectedOrg != null) { // Set the selected organism's position and orientation according to the clamp's hotspot // (Organisms store their location/orientation in a vector/quaternion, so I need to convert // the hotspot matrix into this form. Luckily there's a function!) Matrix clampMatrix = rootCell.GetHotspotMatrix(1); Vector3 clampPosition = new Vector3(clampMatrix.M41, clampMatrix.M42, clampMatrix.M43); clampMatrix.M41 = clampMatrix.M42 = clampMatrix.M43 = 0; Quaternion rot = Quaternion.RotationMatrix(clampMatrix); // NOTE: Assumes there's no scaling factor to mess up the rotation matrix rot.Normalize(); // Normalization improves (but doesn't cure) an error in the rotation using SoftImage SelectedOrg.RotateTo(rot); SelectedOrg.MoveTo(clampPosition); } // If a socket is selected, make sure the socket marker is pointing at it if (SelectedSocket != null) { if (socketMarker == null) { Color c = Color.FromArgb(16, 255, 0, 255); socketMarker = Marker.CreateCone(c, new Vector3(0, 0, 0), new Orientation(), 0.1f, 32.0f); } else { socketMarker.Goto(SelectedSocket.CombinedMatrix); } } else if (socketMarker != null) { socketMarker.Delete(); socketMarker = null; } // If a link socket is selected, make sure the link marker is pointing at it if (LinkSocket != null) { if (linkMarker == null) { Color c = Color.FromArgb(64, 255, 255, 0); linkMarker = Marker.CreateCone(c, new Vector3(0, 0, 0), new Orientation(), 0.3f, 3.5f); } else { linkMarker.Goto(LinkSocket.CombinedMatrix); } } else if (linkMarker != null) { linkMarker.Delete(); linkMarker = null; } // If there's a creature being tractor beamed back to the clamp, update it if (TractorBeam != null) { // Magnetically attract org towards clamp position. If it is now close by, attach it ready for editing Matrix clampMatrix = rootCell.GetHotspotMatrix(1); if (TractorBeam.Tractor(new Vector3(clampMatrix.M41, clampMatrix.M42, clampMatrix.M43), Quaternion.RotationMatrix(clampMatrix)) == true) { Attach(TractorBeam); TractorBeam = null; } } // Pass control to the base method base.Update(); }
/// <summary> /// Attach a creature to the clamp (discarding any previous occupant) /// </summary> /// <param name="org"></param> public void Attach(Organism org) { // detach any previous occupant if (SelectedOrg != null) Detach(); // suspend physical properties, reset CG, etc. and set the selected org, cell & socket org.EditOn(); // Remember this organism so that it can be tractor beamed back to the clamp after release TrackedOrg = org; }
/// <summary> /// TEMP: Create one or more creatures for testing /// </summary> private static void CreateSomeCreatures() { const int NUMCREATURES = 50; Creature = new Organism[NUMCREATURES]; string[] genotype = { "testTail", "testJawRed", }; // Creature 0 is guaranteed to be in front of the camera - use this alone when testing Creature[0] = new Organism(genotype[0], new Vector3(512.0f, 25.0f, 475.0f), new Orientation(0, 0, 0)); Creature[1] = new Organism(genotype[1], new Vector3(512.0f, 25.0f, 480.0f), new Orientation(0, 0, 0)); for (int c = 2; c < NUMCREATURES; c++) { float x, z; do { x = Rnd.Float(400,600); z = Rnd.Float(400,600); } while (Terrain.AltitudeAt(x, z) > Water.WATERLEVEL - 10); Vector3 loc = new Vector3( x, Rnd.Float(Terrain.AltitudeAt(x, z) + 5, Water.WATERLEVEL - 5), z ); Orientation or = new Orientation(Rnd.Float(3.14f), Rnd.Float(3.14f), Rnd.Float(3.14f)); int genome = Rnd.Int(genotype.Length - 1); Creature[c] = new Organism(genotype[genome], loc, or); } }
// Helper for GetObjectsWithinRange() // Run through each object in the given quad, keeping only those that are within the radius // and are of the right type(s). Optionally also filter those not in direct line of sight. private static void GetObjectsFromQuad(Map map, List<IDetectable> result, Vector3 loc, float radius, bool includeOrganisms, bool includeTerrain, bool lineOfSightOnly, Organism ignoreMe) { // Organisms... if (includeOrganisms==true) { foreach (Renderable obj in map.OrganismList) { if (obj != ignoreMe) // don't include the owner of the sensor! { float radii = obj.AbsSphere.Radius + radius; if (Vector3.LengthSq(loc - obj.AbsSphere.Centre) <= (radii * radii)) // include it if spheres intersect { if (lineOfSightOnly == true) // and optionally if unobstructed by terrain etc. { if ((Terrain.InLineOfSight(loc, obj.AbsSphere.Centre)) && (!result.Contains((IDetectable)obj))) // Object may be in several quads, so only add if unique result.Add((IDetectable)obj); } else { if (!result.Contains((IDetectable)obj)) // Object may be in several quads, so only add if unique result.Add((IDetectable)obj); } } } } } // Tiles... if (includeTerrain==true) { foreach (Renderable obj in map.TerrainList) { float radii = obj.AbsSphere.Radius + radius; if (Vector3.LengthSq(loc - obj.AbsSphere.Centre) <= (radii * radii)) // include it if obj sphere intersects tile SPHERE { if (lineOfSightOnly==true) { if (Terrain.InLineOfSight(loc, obj.AbsSphere.Centre)) result.Add((IDetectable)obj); } else { if (!result.Contains((IDetectable)obj)) // Object may be in several quads, so only add if unique result.Add((IDetectable)obj); } } } } }
/// <summary> /// Return a list of all the IDetectable objects within a given radius of a point /// (for sensors). IDetectable objects include Organisms and tiles but not scenery. /// Only return objects of the specified class(es). Optionally perform a line-of-sight /// test on the candidates too. /// </summary> /// <param name="loc">location of sensor</param> /// <param name="radius">range of sensor</param> /// <param name="includeOrganisms">Set true to include organisms in the list</param> /// <param name="includeTerrain">Set true to include tiles in the list</param> /// <param name="lineOfSightOnly">Set true to exclude objects obscured by terrain.</param> /// <param name="ignoreMe">The Organism object issuing the call (or null) - obviously we don't want to detect ourselves!</param> /// <returns>A list of IDetectable objects</returns> public static List<IDetectable> GetObjectsWithinRange(Vector3 loc, float radius, bool includeOrganisms, bool includeTerrain, bool lineOfSightOnly, Organism ignoreMe) { // Create a list to store the winners in List<IDetectable> result = new List<IDetectable>(); // Run down the cell hierarchy, following only the thread that contains the given location, // Until we reach a depth at which the given range exceeds the boundary of the quad. // Back up one level. We now have the smallest quad that completely encloses the circle under consideration... // First, clip the circle to the edges of the map, otherwise no quad will be fully within circle float minx = loc.X-radius; if (minx<0) minx=0; float maxx = loc.X+radius; if (maxx>MapWidth) maxx=MapWidth; float minz = loc.Z-radius; if (minz<0) minz=0; float maxz = loc.Z+radius; if (maxz>MapHeight) maxz=MapHeight; // then walk the hierarchy Map map = root.RecursiveFindSmallestContainer(minx, maxx, minz, maxz); // normally we can just search the one quad if (map.level!=0) { GetObjectsFromQuad(map, result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); } // BUT, for a sensor close to the mid-point of the map, the quad to search will be the WHOLE map!!! // This is sufficiently wasteful that it's worth treating as a special case (reduces workload by factor of 4)... else { if ((map.child[0].child[2].bounds.Right >= maxx) &&(map.child[1].child[3].bounds.Y <= minz) &&(map.child[2].child[0].bounds.X <= minx) &&(map.child[3].child[1].bounds.Bottom >= maxz)) { // only scan the grandchildren that surround the mid-point GetObjectsFromQuad(map.child[0].child[2], result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); GetObjectsFromQuad(map.child[1].child[3], result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); GetObjectsFromQuad(map.child[2].child[0], result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); GetObjectsFromQuad(map.child[3].child[1], result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); } else { // here if there was some other reason for having to scan the whole map - e.g. very large range effectors GetObjectsFromQuad(map.child[0].child[2], result, loc, radius, includeOrganisms, includeTerrain, lineOfSightOnly, ignoreMe); } } return result; }