protected override void OnPositionChanged(Quat rotation, float angle, Vector3 membraneCoords) { organelle.OrganelleGraphics.Transform = new Transform(rotation, membraneCoords); Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); Vector3 membranePointDirection = (membraneCoords - middle).Normalized(); membraneCoords += membranePointDirection * Constants.DEFAULT_HEX_SIZE * 2; if (organelle.ParentMicrobe.Species.IsBacteria) { membraneCoords *= 0.5f; } var physicsRotation = MathUtils.CreateRotationForPhysicsOrganelle(angle); var parentMicrobe = currentShapesParent; if (parentMicrobe.Colony != null && !NeedsUpdateAnyway()) { // Get the real position of the pilus while in the colony membraneCoords = organelle.RotatedPositionInsideColony(membraneCoords); membraneCoords += parentMicrobe.GetOffsetRelativeToMaster(); } var transform = new Transform(physicsRotation, membraneCoords); if (NeedsUpdateAnyway()) { CreateShape(parentMicrobe); } currentShapesParent.ShapeOwnerSetTransform(addedChildShapes[0], transform); }
public virtual void Update(float elapsed) { // TODO: it would be nicer if this were notified when the // membrane changes to not recheck this constantly Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); var delta = middle - organellePos; Vector3 exit = middle - delta; var membraneCoords = organelle.ParentMicrobe.Membrane.GetExternalOrganelle(exit.x, exit.z); if (!membraneCoords.Equals(lastCalculatedPos) || NeedsUpdateAnyway()) { float angle = Mathf.Atan2(-delta.z, delta.x); if (angle < 0) { angle = angle + (2 * Mathf.Pi); } angle = (angle * 180 / Mathf.Pi - 90) % 360; var rotation = MathUtils.CreateRotationForExternal(angle); OnPositionChanged(rotation, angle, membraneCoords); lastCalculatedPos = membraneCoords; } }
public static float CalculateSpeed(IEnumerable <OrganelleTemplate> organelles, MembraneType membraneType, float membraneRigidity) { float microbeMass = Constants.MICROBE_BASE_MASS; float organelleMovementForce = 0; Vector3 forwardsDirection = new Vector3(0, 0, -1); foreach (var organelle in organelles) { microbeMass += organelle.Definition.Mass; if (organelle.Definition.HasComponentFactory <MovementComponentFactory>()) { Vector3 organelleDirection = (Hex.AxialToCartesian(new Hex(0, 0)) - Hex.AxialToCartesian(organelle.Position)).Normalized(); float directionFactor = organelleDirection.Dot(forwardsDirection); organelleMovementForce += Constants.FLAGELLA_BASE_FORCE * organelle.Definition.Components.Movement.Momentum / 100.0f * directionFactor; } } float baseMovementForce = Constants.CELL_BASE_THRUST * (membraneType.MovementFactor - membraneRigidity * Constants.MEMBRANE_RIGIDITY_MOBILITY_MODIFIER); float finalSpeed = (baseMovementForce + organelleMovementForce) / microbeMass; return(finalSpeed); }
public void OnAttachToCell(PlacedOrganelle organelle) { this.organelle = organelle; organellePos = Hex.AxialToCartesian(organelle.Position); CustomAttach(); }
public virtual void Update(float elapsed) { // TODO: it would be nicer if this were notified when the // membrane changes to not recheck this constantly Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); var relativeOrganellePosition = middle - organellePos; if (relativeOrganellePosition == Vector3.Zero) { relativeOrganellePosition = DefaultVisualPos; } Vector3 exit = middle - relativeOrganellePosition; var membraneCoords = organelle.ParentMicrobe.Membrane.GetVectorTowardsNearestPointOfMembrane(exit.x, exit.z); if (!membraneCoords.Equals(lastCalculatedPosition) || NeedsUpdateAnyway()) { float angle = GetAngle(relativeOrganellePosition); var rotation = MathUtils.CreateRotationForExternal(angle); OnPositionChanged(rotation, angle, membraneCoords); lastCalculatedPosition = membraneCoords; } }
/// <summary> /// Calculate the momentum of the movement organelle based on /// angle towards middle of cell /// </summary> private static Vector3 CalculateForce(Hex pos, float momentum) { Vector3 organelle = Hex.AxialToCartesian(pos); Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); var delta = middle - organelle; return(delta.Normalized() * momentum); }
protected override void OnPositionChanged(Quat rotation, float angle, Vector3 membraneCoords) { organelle.OrganelleGraphics.Transform = new Transform(rotation, membraneCoords); Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); Vector3 membranePointDirection = (membraneCoords - middle).Normalized(); membraneCoords += membranePointDirection * Constants.DEFAULT_HEX_SIZE * 2; if (organelle.ParentMicrobe.Species.IsBacteria) { membraneCoords *= 0.5f; } float pilusSize = 4.6f; // Scale the size down for bacteria if (organelle.ParentMicrobe.Species.IsBacteria) { pilusSize *= 0.5f; } var physicsRotation = MathUtils.CreateRotationForPhysicsOrganelle(angle); // Need to remove the old copy first DestroyShape(); // TODO: Godot doesn't have Cone shape. // https://github.com/godotengine/godot-proposals/issues/610 // So this uses a cylinder for now // @pilusShape = organelle.world.GetPhysicalWorld().CreateCone(pilusSize / 10.f, // pilusSize); var shape = new CylinderShape(); shape.Radius = pilusSize / 10.0f; shape.Height = pilusSize; var parentMicrobe = organelle.ParentMicrobe; var ownerId = parentMicrobe.CreateShapeOwner(shape); parentMicrobe.ShapeOwnerAddShape(ownerId, shape); // TODO: find a way to pass the information to the shape / // parentMicrobe what is a pilus part of the collision // pilusShape.SetCustomTag(PHYSICS_PILUS_TAG); var transform = new Transform(physicsRotation, membraneCoords); parentMicrobe.ShapeOwnerSetTransform(ownerId, transform); parentMicrobe.AddPilus(ownerId); addedChildShapes.Add(ownerId); }
/// <summary> /// Calculate the momentum of the movement organelle based on /// angle towards middle of cell /// If the flagella is placed in the microbe's center, hence delta equals 0, /// consider defaultPos as the organelle's "false" position. /// </summary> private static Vector3 CalculateForce(Hex pos, float momentum) { Vector3 organellePosition = Hex.AxialToCartesian(pos); Vector3 middle = Hex.AxialToCartesian(new Hex(0, 0)); var delta = middle - organellePosition; if (delta == Vector3.Zero) { delta = DefaultVisualPos; } return(delta.Normalized() * momentum); }
public Vector3 CalculateCenterOffset() { var offset = new Vector3(0, 0, 0); foreach (var hex in Hexes) { offset += Hex.AxialToCartesian(hex); } offset /= Hexes.Count; return(offset); }
private void SetupOrganelleGraphics() { var organelleSceneInstance = (Spatial)Definition.LoadedScene.Instance(); // Store the material of the organelle to be updated GeometryInstance geometry; // Fetch the actual model from the scene to get at the material we set the tint on if (string.IsNullOrEmpty(Definition.DisplaySceneModelPath)) { geometry = (GeometryInstance)organelleSceneInstance; } else { geometry = organelleSceneInstance.GetNode <GeometryInstance>(Definition.DisplaySceneModelPath); } // Store animation player for later use if (!string.IsNullOrEmpty(Definition.DisplaySceneAnimation)) { OrganelleAnimation = organelleSceneInstance.GetNode <AnimationPlayer>(Definition.DisplaySceneAnimation); } organelleMaterial = (ShaderMaterial)geometry.MaterialOverride; // There is an intermediate node so that the organelle scene root rotation and scale work OrganelleGraphics = new Spatial(); OrganelleGraphics.AddChild(organelleSceneInstance); AddChild(OrganelleGraphics); OrganelleGraphics.Scale = new Vector3(Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE); // Position the intermediate node relative to origin of cell var transform = new Transform(Quat.Identity, Hex.AxialToCartesian(Position) + Definition.CalculateModelOffset()); OrganelleGraphics.Transform = transform; // For some reason MathUtils.CreateRotationForOrganelle(Orientation) in the above transform doesn't work OrganelleGraphics.RotateY((Orientation * -60) * MathUtils.DEGREES_TO_RADIANS); }
public static Vector3 GetVector(int orientation) { Hex hex0 = new Hex(0, 0); Hex hex1 = new Hex(0, 0); if (orientation == 0) { hex1 = new Hex(0, 1); } else if (orientation == 5) { hex1 = new Hex(1, 0); } else if (orientation == 4) { hex1 = new Hex(1, -1); } else if (orientation == 3) { hex1 = new Hex(0, -1); } else if (orientation == 2) { hex1 = new Hex(-1, 0); } else if (orientation == 1) { hex1 = new Hex(-1, 1); } Vector3 hex0v = Hex.AxialToCartesian(hex0); Vector3 hex1v = Hex.AxialToCartesian(hex1); Vector3 hexdif = hex1v - hex0v; hexdif = hexdif.Normalized(); GD.Print("hex dif: (", hexdif.x, ", ", hexdif.y, ", ", hexdif.z, ")"); return(hexdif); }
/// <summary> /// Calculates a world pos for emitting compounds /// </summary> private Vector3 CalculateNearbyWorldPosition() { // The back of the microbe var exit = Hex.AxialToCartesian(new Hex(0, 1)); var membraneCoords = Membrane.GetVectorTowardsNearestPointOfMembrane(exit.x, exit.z); // Get the distance to eject the compounds var ejectionDistance = Membrane.EncompassingCircleRadius; // The membrane radius doesn't take being bacteria into account if (Species.IsBacteria) { ejectionDistance *= 0.5f; } float angle = 180; // Find the direction the microbe is facing var yAxis = Transform.basis.y; var microbeAngle = Mathf.Atan2(yAxis.x, yAxis.y); if (microbeAngle < 0) { microbeAngle += 2 * Mathf.Pi; } microbeAngle = microbeAngle * 180 / Mathf.Pi; // Take the microbe angle into account so we get world relative degrees var finalAngle = (angle + microbeAngle) % 360; var s = Mathf.Sin(finalAngle / 180 * Mathf.Pi); var c = Mathf.Cos(finalAngle / 180 * Mathf.Pi); var ejectionDirection = new Vector3(-membraneCoords.x * c + membraneCoords.z * s, 0, membraneCoords.x * s + membraneCoords.z * c); return(Translation + (ejectionDirection * ejectionDistance)); }
public float CalculateSpeed() { float microbeMass = Constants.MICROBE_BASE_MASS; float baseMovementForce = 0; float organelleMovementForce = 0; Vector3 forwardsDirection = new Vector3(0, 0, -1); foreach (var organelle in editedMicrobeOrganelles.Organelles) { microbeMass += organelle.Definition.Mass; if (organelle.Definition.HasComponentFactory<MovementComponentFactory>()) { Vector3 organelleDirection = (Hex.AxialToCartesian(new Hex(0, 0)) - Hex.AxialToCartesian(organelle.Position)).Normalized(); float directionFactor = organelleDirection.Dot(forwardsDirection); // Flagella pointing backwards don't slow you down directionFactor = Math.Max(directionFactor, 0); organelleMovementForce += Constants.FLAGELLA_BASE_FORCE * organelle.Definition.Components.Movement.Momentum / 100.0f * directionFactor; } } baseMovementForce = Constants.CELL_BASE_THRUST * (Membrane.MovementFactor - Rigidity * Constants.MEMBRANE_RIGIDITY_MOBILITY_MODIFIER); float finalSpeed = (baseMovementForce + organelleMovementForce) / microbeMass; return finalSpeed; }
/// <summary> /// This destroys and creates again entities to represent all /// the currently placed organelles. Call this whenever /// editedMicrobeOrganelles is changed. /// </summary> private void UpdateAlreadyPlacedVisuals() { int nextFreeHex = 0; int nextFreeOrganelle = 0; // Build the entities to show the current microbe foreach (var organelle in editedMicrobeOrganelles.Organelles) { foreach (var hex in organelle.RotatedHexes) { var pos = Hex.AxialToCartesian(hex + organelle.Position); if (nextFreeHex >= placedHexes.Count) { // New hex needed placedHexes.Add(CreateEditorHex()); } var hexNode = placedHexes[nextFreeHex++]; hexNode.MaterialOverride = validMaterial; hexNode.Translation = pos; hexNode.Visible = true; } // Model of the organelle if (organelle.Definition.DisplayScene != null) { var pos = Hex.AxialToCartesian(organelle.Position) + organelle.Definition.CalculateModelOffset(); if (nextFreeOrganelle >= placedModels.Count) { // New organelle model needed placedModels.Add(CreateEditorOrganelle()); } var organelleModel = placedModels[nextFreeOrganelle++]; organelleModel.Transform = new Transform( MathUtils.CreateRotationForOrganelle(1 * organelle.Orientation), pos); organelleModel.Scale = new Vector3(Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE); organelleModel.Visible = true; UpdateOrganellePlaceHolderScene(organelleModel, organelle.Definition.DisplayScene); } } // Delete excess entities while (nextFreeHex < placedHexes.Count) { placedHexes[placedHexes.Count - 1].QueueFree(); placedHexes.RemoveAt(placedHexes.Count - 1); } while (nextFreeOrganelle < placedModels.Count) { placedModels[placedModels.Count - 1].QueueFree(); placedModels.RemoveAt(placedModels.Count - 1); } }
/// <summary> /// Called by a microbe when this organelle has been added to it /// </summary> public void OnAddedToMicrobe(Microbe microbe) { if (Definition == null) { throw new Exception("PlacedOrganelle has no definition set"); } if (ParentMicrobe != null) { throw new InvalidOperationException("PlacedOrganelle is already in a microbe"); } // Store parameters ParentMicrobe = microbe; // Grab the species colour for us Colour = microbe.Species.Colour; ParentMicrobe.OrganelleParent.AddChild(this); // Graphical display if (Definition.LoadedScene != null) { // There is an intermediate node so that the organelle scene root rotation and scale work OrganelleGraphics = new Spatial(); var organelleSceneInstance = (Spatial)Definition.LoadedScene.Instance(); AddChild(OrganelleGraphics); OrganelleGraphics.Scale = new Vector3(Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE); var transform = new Transform(MathUtils.CreateRotationForOrganelle(Orientation), Definition.CalculateModelOffset()); OrganelleGraphics.Transform = transform; // Store the material of the organelle to be updated GeometryInstance geometry; // Fetch the actual model from the scene to get at the material we set the tint on if (string.IsNullOrEmpty(Definition.DisplaySceneModelPath)) { geometry = (GeometryInstance)organelleSceneInstance; } else { geometry = organelleSceneInstance.GetNode <GeometryInstance>(Definition.DisplaySceneModelPath); } // Store animation player for later use if (!string.IsNullOrEmpty(Definition.DisplaySceneAnimation)) { OrganelleAnimation = organelleSceneInstance.GetNode <AnimationPlayer>(Definition.DisplaySceneAnimation); } organelleMaterial = (ShaderMaterial)geometry.MaterialOverride; OrganelleGraphics.AddChild(organelleSceneInstance); } // Position relative to origin of cell RotateY(Orientation * 60); Translation = Hex.AxialToCartesian(Position); float hexSize = Constants.DEFAULT_HEX_SIZE; // Scale the physics hex size down for bacteria if (microbe.Species.IsBacteria) { hexSize *= 0.5f; } // Physics ParentMicrobe.Mass += Definition.Mass; // Add hex collision shapes foreach (Hex hex in Definition.GetRotatedHexes(Orientation)) { var shape = new SphereShape(); shape.Radius = hexSize * 2.0f; var ownerId = ParentMicrobe.CreateShapeOwner(shape); // This is needed to actually add the shape ParentMicrobe.ShapeOwnerAddShape(ownerId, shape); // The shape is in our parent so the final position is our // offset plus the hex offset Vector3 shapePosition = Hex.AxialToCartesian(hex) + Translation; // Scale for bacteria physics. if (microbe.Species.IsBacteria) { shapePosition *= 0.5f; } var transform = new Transform(Quat.Identity, shapePosition); ParentMicrobe.ShapeOwnerSetTransform(ownerId, transform); shapes.Add(ownerId); } // Components Components = new List <IOrganelleComponent>(); foreach (var factory in Definition.ComponentFactories) { var component = factory.Create(); if (component == null) { throw new Exception("PlacedOrganelle component factory returned null"); } component.OnAttachToCell(this); Components.Add(component); } ResetGrowth(); }
/// <summary> /// Called by a microbe when this organelle has been added to it /// </summary> public void OnAddedToMicrobe(Microbe microbe) { if (Definition == null) { throw new Exception("PlacedOrganelle has no definition set"); } if (ParentMicrobe != null) { throw new InvalidOperationException("PlacedOrganelle is already in a microbe"); } // Store parameters ParentMicrobe = microbe; // Grab the species colour for us Colour = microbe.Species.Colour; ParentMicrobe.OrganelleParent.AddChild(this); // Graphical display if (Definition.LoadedScene != null) { SetupOrganelleGraphics(); } float hexSize = Constants.DEFAULT_HEX_SIZE; // Scale the physics hex size down for bacteria if (microbe.Species.IsBacteria) { hexSize *= 0.5f; } // Physics ParentMicrobe.Mass += Definition.Mass; // Add hex collision shapes foreach (Hex hex in Definition.GetRotatedHexes(Orientation)) { var shape = new SphereShape(); shape.Radius = hexSize * 2.0f; var ownerId = ParentMicrobe.CreateShapeOwner(shape); // This is needed to actually add the shape ParentMicrobe.ShapeOwnerAddShape(ownerId, shape); // The shape is in our parent so the final position is our // offset plus the hex offset Vector3 shapePosition = Hex.AxialToCartesian(hex) + Translation; // Scale for bacteria physics. if (microbe.Species.IsBacteria) { shapePosition *= 0.5f; } var transform = new Transform(Quat.Identity, shapePosition); ParentMicrobe.ShapeOwnerSetTransform(ownerId, transform); shapes.Add(ownerId); } // Components Components = new List <IOrganelleComponent>(); foreach (var factory in Definition.ComponentFactories) { var component = factory.Create(); if (component == null) { throw new Exception("PlacedOrganelle component factory returned null"); } component.OnAttachToCell(this); Components.Add(component); } ResetGrowth(); }
/// <summary> /// If not hovering over an organelle, render the to-be-placed organelle /// </summary> private void RenderHighlightedOrganelle(int q, int r, int rotation) { if (ActiveActionName == null) { return; } // TODO: this should be changed into a function parameter var toBePlacedOrganelle = SimulationParameters.Instance.GetOrganelleType( ActiveActionName); bool showModel = true; foreach (var hex in toBePlacedOrganelle.GetRotatedHexes(rotation)) { int posQ = hex.Q + q; int posR = hex.R + r; var pos = Hex.AxialToCartesian(new Hex(posQ, posR)); // Detect can it be placed there bool canPlace = isPlacementProbablyValid; bool duplicate = false; // Skip if there is a placed organelle here already foreach (var placed in placedHexes) { if ((pos - placed.Translation).LengthSquared() < 0.001f) { duplicate = true; if (!canPlace) { // Mark as invalid placed.MaterialOverride = invalidMaterial; showModel = false; } break; } } if (duplicate) { continue; } var hoverHex = hoverHexes[usedHoverHex++]; hoverHex.Translation = pos; hoverHex.Visible = true; hoverHex.MaterialOverride = canPlace ? validMaterial : invalidMaterial; } // Model if (!string.IsNullOrEmpty(toBePlacedOrganelle.DisplayScene) && showModel) { var cartesianPosition = Hex.AxialToCartesian(new Hex(q, r)); var organelleModel = hoverOrganelles[usedHoverOrganelle++]; organelleModel.Transform = new Transform( MathUtils.CreateRotationForOrganelle(rotation), cartesianPosition + toBePlacedOrganelle.CalculateModelOffset()); organelleModel.Scale = new Vector3(Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE, Constants.DEFAULT_HEX_SIZE); organelleModel.Visible = true; UpdateOrganellePlaceHolderScene(organelleModel, toBePlacedOrganelle.DisplayScene); } }
public static Vector3 GetOrganelleDirection(OrganelleTemplate organelle) { return((Hex.AxialToCartesian(new Hex(0, 0)) - Hex.AxialToCartesian(organelle.Position)).Normalized()); }