Example #1
0
        private static Matrix3D GetProjectionMatrix(PerspectiveCamera camera, double aspectRatio)
        {
            if (camera == null)
            {
                throw new ArgumentNullException("camera");
            }

            // This math is identical to what you find documented for
            // D3DXMatrixPerspectiveFovRH with the exception that in
            // WPF the camera's horizontal rather the vertical
            // field-of-view is specified.

            double hFoV = Math1D.DegreesToRadians(camera.FieldOfView);
            double zn   = camera.NearPlaneDistance;
            double zf   = camera.FarPlaneDistance;

            double xScale = 1 / Math.Tan(hFoV / 2);
            double yScale = aspectRatio * xScale;
            double m33    = (zf == double.PositiveInfinity) ? -1 : (zf / (zn - zf));
            double m43    = zn * m33;

            return(new Matrix3D(
                       xScale, 0, 0, 0,
                       0, yScale, 0, 0,
                       0, 0, m33, -1,
                       0, 0, m43, 0));
        }
        private static void AddArc(ScreenSpaceLines3D line, Point3D center, double radius, int segments, double startAngle, double stopAngle, bool closeEnd)
        {
            startAngle = Math1D.DegreesToRadians(startAngle);
            stopAngle  = Math1D.DegreesToRadians(stopAngle);

            // swap angles
            if (startAngle > stopAngle)
            {
                double temp = startAngle;
                startAngle = stopAngle;
                stopAngle  = temp;
            }

            Point3D[] points = new Point3D[segments + 1];
            double    inc    = (stopAngle - startAngle) / segments;

            double r = startAngle;

            for (int i = 0; i <= segments; i++, r += inc)
            {
                points[i] = new Point3D(
                    center.X + (Math.Cos(-r) * radius),
                    center.Y + (Math.Sin(-r) * radius),
                    center.Z);
            }

            line.AddPolygon(closeEnd, points);
        }
Example #3
0
        private void PhysicsBody_ApplyForceAndTorque(object sender, BodyApplyForceAndTorqueArgs e)
        {
            // See if there is anything to do
            if (_desiredOrientation == null)
            {
                return;
            }

            //TODO: Implement this.Offset
            //TODO: Allow rotations in the destination axis

            Vector3D   current  = e.Body.DirectionToWorld(new Vector3D(0, 0, 1));
            Quaternion rotation = Math3D.GetRotation(current, _desiredOrientation.Value);

            if (rotation.IsIdentity)
            {
                // Don't set anything.  If they are rotating along the allowed axis, then no problem.  If they try
                // to rotate off that axis, another iteration of this method will rotate back
                //e.Body.AngularVelocity = new Vector3D(0, 0, 0);
                return;
            }

            // According to the newton wiki, angular velociy is radians per second
            Vector3D newAngVel = rotation.Axis.ToUnit() * Math1D.DegreesToRadians(rotation.Angle);

            newAngVel *= _multiplier;

            if (this.MaxVelocity != null && newAngVel.LengthSquared > this.MaxVelocity.Value * this.MaxVelocity.Value)
            {
                newAngVel = newAngVel.ToUnit() * this.MaxVelocity.Value;
            }

            e.Body.AngularVelocity = newAngVel;
        }
Example #4
0
        /// <summary>
        /// This defines the back side of a cylinder
        /// </summary>
        private static Geometry3D GetGeometry(int numSegments, double thetaOffset, double height, double radius, double translate)
        {
            double thetaStart = 270 - thetaOffset;
            double thetaStop  = 270 + thetaOffset;

            MeshGeometry3D retVal = new MeshGeometry3D();

            #region Initial calculations

            // The rest of this method has height along Z, but the final needs to go along Y
            Transform3DGroup transform = new Transform3DGroup();
            transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 90d)));
            //transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180d)));
            transform.Children.Add(new TranslateTransform3D(0, 0, translate));

            double halfHeight = height / 2d;

            Point[] points = new Point[numSegments];

            double deltaTheta = Math1D.DegreesToRadians((thetaStop - thetaStart) / (numSegments - 1));        //NOTE: This will fail if theta start/stop goes past 0/360
            double theta      = Math1D.DegreesToRadians(thetaStart);

            for (int cntr = 0; cntr < numSegments; cntr++)
            {
                points[cntr] = new Point(Math.Cos(theta) * radius, Math.Sin(theta) * radius);
                theta       += deltaTheta;
            }

            #endregion

            for (int cntr = 0; cntr < numSegments; cntr++)
            {
                retVal.Positions.Add(transform.Transform(new Point3D(points[cntr].X, points[cntr].Y, -halfHeight)));
                retVal.Positions.Add(transform.Transform(new Point3D(points[cntr].X, points[cntr].Y, halfHeight)));

                retVal.Normals.Add(transform.Transform(new Vector3D(-points[cntr].X, -points[cntr].Y, 0d)));            // the normals point straight in from the side
                retVal.Normals.Add(transform.Transform(new Vector3D(-points[cntr].X, -points[cntr].Y, 0d)));

                double coord = Convert.ToDouble(cntr) / Convert.ToDouble(numSegments - 1);
                retVal.TextureCoordinates.Add(new Point(coord, 0));
                retVal.TextureCoordinates.Add(new Point(coord, 1));
            }

            for (int cntr = 0; cntr < numSegments - 1; cntr++)
            {
                // 0,2,3
                retVal.TriangleIndices.Add((cntr * 2) + 0);
                retVal.TriangleIndices.Add((cntr * 2) + 3);
                retVal.TriangleIndices.Add((cntr * 2) + 2);

                // 0,3,1
                retVal.TriangleIndices.Add((cntr * 2) + 0);
                retVal.TriangleIndices.Add((cntr * 2) + 1);
                retVal.TriangleIndices.Add((cntr * 2) + 3);
            }

            return(retVal);
        }
Example #5
0
        private void Brain_StraightToTarget_VelocityAware2()
        {
            // Velocity should be zero when touching the chase point

            //TODO:  Implement this (when not attacking, velocity should slow to a max speed the closer to the chase point)



            // Convert everything to local coords
            Point3D position   = new Point3D(0, 0, 0);
            Point3D chasePoint = this.PhysicsBody.PositionFromWorld(_chasePoint);

            Vector3D directionToGo = chasePoint.ToVector() - position.ToVector();

            Quaternion rotation;

            #region Adjust for current velocity attempt1a

            Vector3D currentVelocity = this.VelocityWorld;

            if (!Math1D.IsNearZero(currentVelocity.LengthSquared))
            {
                currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity);

                rotation = Math3D.GetRotation(directionToGo, currentVelocity);

                // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned,
                // the angle will be zero, so negating won't make a difference)
                rotation = rotation.ToReverse();

                // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line
                // eventually)  I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit
                if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d)
                {
                    // Change the direction by the angle
                    directionToGo = rotation.GetRotatedVector(directionToGo);
                }
            }

            #endregion

            // Now that I know where to go, rotate the original thruster direction (0,1,0) to line up with the desired direction
            rotation = Math3D.GetRotation(_origThrustDirection, directionToGo);

            // Thrust Direction
            _thrustTransform = new RotateTransform3D(new QuaternionRotation3D(rotation));

            // Thrust Strength
            if (_isAttacking)
            {
                _thrustPercent = 1d;
            }
            else
            {
                _thrustPercent = .5d;
            }
        }
Example #6
0
        /// <summary>
        /// This will emulate sounds coming from locations relative to the listener
        /// </summary>
        /// <remarks>
        /// Note that sound intensity doesn't diminish linearly from by distance, but by:
        ///    I = P/(4*pi*r^2)
        ///
        /// I is final intensity
        /// P is intensity at the source
        /// r is distance from the listener
        /// </remarks>
        /// <param name="volume">0 (none) to 1 (full)</param>
        /// <param name="balance">-1 (all left) to 1 (all right)</param>
        /// <param name="offset">The location of the sound relative to the listener</param>
        /// <param name="sourceVolume">0 (none) to 1 (full) - this is how loud the source would be at the listener</param>
        private void GetPositionalSoundSettings(out double volume, out double balance, Vector3D offset, double sourceVolume)
        {
            #region Calculate Volume

            // I won't range check the input volume - I'll leave it up to the media player to either bomb or cap it

            double intensityRatio = 1d;
            double offsetLength   = offset.Length;
            if (_distanceFalloffRatio == 0d || Math1D.IsNearZero(offsetLength))
            {
                volume = sourceVolume;
            }
            else
            {
                double distance = offsetLength * _distanceFalloffRatio;

                intensityRatio = 1d / (4d * Math.PI * distance * distance);    // I need this intensity when calculating balance
                volume         = sourceVolume * intensityRatio;
            }

            #endregion

            #region Calculate Balance

            // This means that if a sound is a distance of very near zero, and all the way to the left or right, then instead of +-1, it is +-.5
            const double MAXOPPOSITE_EAR = .5d;
            // When the intensity would be 75% of max, then I won't add anything to the opposite ear
            const double MAXOPPOSITE_DISTANCEINTENSITY = .75d;

            // Getting the angle (I don't want Z)
            double angle = Vector3D.AngleBetween(new Vector3D(1, 0, 0), new Vector3D(offset.X, offset.Y, 0));

            // cos(0) is 1, cos(90) is 0, cos(180) is -1.  Exactly what I need
            balance = Math.Cos(Math1D.DegreesToRadians(angle));

            //NOTE:  The problem with a pure cosine is that if a loud sound is sitting very near the person, but on the left, then in reality, the right
            // ear would hear something, but a simple cosine would be all the way -1
            //
            // So, I'm going to approach .5 for objects that are near (still on the left, but can be heard on the right)
            if (intensityRatio > MAXOPPOSITE_DISTANCEINTENSITY)
            {
                // So when the intensity is 1 (right next to the head), then I will give back .5
                // When the intensity is at the limit, then balance will be the pure cosine value
                // Anywhere in between is a linear interpelation
                balance = UtilityCore.GetScaledValue_Capped(balance, MAXOPPOSITE_EAR * balance, MAXOPPOSITE_DISTANCEINTENSITY, 1d, intensityRatio);
            }

            #endregion

            //TODO:  Take in relative velocity, then manipulate playback speed to emulate doppler (unless there is a way to change pitch directly)
        }
Example #7
0
        protected override CJoint OnInitialise()
        {
            CJointBallSocket joint = new CJointBallSocket(this.World.NewtonWorld);

            joint.NewtonConstraintCreateBall(
                (Vector3D)BodyToWorld(this.ChildBody, this.PivotPoint),
                this.ChildBody.NewtonBody,
                this.ParentBody.NewtonBody);

            joint.BallSetConeLimits(
                BodyToWorld(this.ChildBody, Direction),
                (float)Math1D.DegreesToRadians(MaxConeAngle),
                (float)Math1D.DegreesToRadians(MaxTwistAngle));

            joint.Ball += InternalUserCallback;

            return(joint);
        }
Example #8
0
        protected void Update()
        {
            if (IsInitialised)
            {
                if (IsInitialising)
                {
                    _needsUpdate = true;
                }
                else
                {
                    NetwonJoint.BallSetConeLimits(
                        BodyToWorld(this.ChildBody, Direction),
                        (float)Math1D.DegreesToRadians(MaxConeAngle),
                        (float)Math1D.DegreesToRadians(MaxTwistAngle));

                    _needsUpdate = false;
                }
            }
        }
Example #9
0
        private void joint_Hinge(object sender, CHingeEventArgs e)
        {
            double newAngle = Math1D.RadiansToDegrees(NewtonJoint.HingeAngle);
            double angle    = this.Angle + AngleDiffence(this.Angle, newAngle);

            SetValue(AnglePropertyKey, angle);

            if (this.SetAngle != null)
            {
                e.Desc.m_Accel = NewtonJoint.HingeCalculateStopAlpha(e.Desc,
                                                                     Math1D.DegreesToRadians((float)MathUtils.MinMax(this.MinAngle, (double)this.SetAngle, this.MaxAngle)))

                                 * (float)this.SetAngleStiffness;
                e.ApplyConstraint = true;
            }
            else if ((this.MinAngle != null) && (angle < (double)this.MinAngle))
            {
                e.Desc.m_Accel = NewtonJoint.HingeCalculateStopAlpha(e.Desc,
                                                                     (float)Math1D.DegreesToRadians((double)this.MinAngle));
                e.ApplyConstraint = true;
            }
            else if ((this.MaxAngle != null) && (angle > (double)this.MaxAngle))
            {
                e.Desc.m_Accel = NewtonJoint.HingeCalculateStopAlpha(e.Desc,
                                                                     (float)Math1D.DegreesToRadians((double)this.MaxAngle));
                e.ApplyConstraint = true;
            }
            else
            {
                if (this.AngularDamperning != 0)
                {
                    // -(NewtonJoint.HingeOmega * e.Desc.m_Timestep * (float)this.AngularDamperning);
                    e.Desc.m_Accel    = -(NewtonJoint.HingeOmega / e.Desc.m_Timestep) * (float)AngularDamperning;
                    e.ApplyConstraint = true;
                }

                if (this.Torque != 0)
                {
                    e.Desc.m_Accel   += (float)this.Torque;
                    e.ApplyConstraint = true;
                }
            }
        }
Example #10
0
        private Vector3D StraightToTarget_VelocityAware1Worker(Point3D chasePointWorld)
        {
            // Convert everything to local coords
            Point3D position        = new Point3D(0, 0, 0);
            Point3D chasePointLocal = this.PhysicsBody.PositionFromWorld(chasePointWorld);

            Vector3D directionToGo = chasePointLocal.ToVector() - position.ToVector();

            Vector3D axis;
            double   radians;

            #region Adjust for current velocity attempt1a

            Vector3D currentVelocity = this.VelocityWorld;

            if (!Math1D.IsNearZero(currentVelocity.LengthSquared))
            {
                currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity);

                Quaternion rotation = Math3D.GetRotation(directionToGo, currentVelocity);

                // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned,
                // the angle will be zero, so negating won't make a difference)
                rotation = rotation.ToReverse();

                // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line
                // eventually)  I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit
                if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d)
                {
                    // Change the direction by the angle
                    directionToGo = rotation.GetRotatedVector(directionToGo);
                }
            }

            #endregion

            // Exit Function
            return(directionToGo);
        }
Example #11
0
        /// <summary>
        /// Set the properties, then call create
        /// NOTE:  This adds itself to the viewport and world.  In the future, that should be handled by the caller
        /// </summary>
        public void CreateShip(MaterialManager materialManager, SharedVisuals sharedVisuals, Map map, ProgressBarGame progressBarCargo, ProgressBarGame progressBarFuel)
        {
            const double THRUSTLINELENGTH       = .5d;
            const double THRUSTLINELENGTH_EXTRA = .75d;
            const double THRUSTLINELENGTH_TURN  = .3;

            _sharedVisuals    = sharedVisuals;
            _map              = map;
            _progressBarCargo = progressBarCargo;
            _progressBarFuel  = progressBarFuel;

            #region Thrusters

            // These need to be definded before the visuals are created
            double radians = Math1D.DegreesToRadians(225);
            _thrusterOffset_BottomLeft = new Vector3D(this.RadiusX * Math.Cos(radians), this.RadiusY * Math.Sin(radians), 0);
            radians = Math1D.DegreesToRadians(135);
            _thrusterOffset_TopLeft = new Vector3D(this.RadiusX * Math.Cos(radians), this.RadiusY * Math.Sin(radians), 0);

            radians = Math1D.DegreesToRadians(315);
            _thrusterOffset_BottomRight = new Vector3D(this.RadiusX * Math.Cos(radians), this.RadiusY * Math.Sin(radians), 0);
            radians = Math1D.DegreesToRadians(45);
            _thrusterOffset_TopRight = new Vector3D(this.RadiusX * Math.Cos(radians), this.RadiusY * Math.Sin(radians), 0);

            _thrustForce = 100d;
            _torqueballLeftRightThrusterForce = -10d;

            #region Define ThrustLines

            //NOTE: The ThrustLine class will add/remove visuals directly from the viewport.  There's no need for the map to get involved

            // The reference to the fuel tank will be made when I create the tank

            ThrustLine thrustLine;

            // Up
            _thrustLines.Add(Key.Up, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * _thrustForce, _thrusterOffset_BottomRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.Up].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * _thrustForce, _thrusterOffset_BottomLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.Up].Add(thrustLine);

            // W
            _thrustLines.Add(Key.W, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * (_thrustForce * 2d), _thrusterOffset_BottomRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_EXTRA;
            _thrustLines[Key.W].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * (_thrustForce * 2d), _thrusterOffset_BottomLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_EXTRA;
            _thrustLines[Key.W].Add(thrustLine);

            // Down
            _thrustLines.Add(Key.Down, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * _thrustForce, _thrusterOffset_TopRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.Down].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * _thrustForce, _thrusterOffset_TopLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.Down].Add(thrustLine);

            // S
            _thrustLines.Add(Key.S, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * (_thrustForce * 2d), _thrusterOffset_TopRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_EXTRA;
            _thrustLines[Key.S].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * (_thrustForce * 2d), _thrusterOffset_TopLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_EXTRA;
            _thrustLines[Key.S].Add(thrustLine);

            // Left
            _thrustLines.Add(Key.Left, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * _torqueballLeftRightThrusterForce, _thrusterOffset_BottomRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_TURN;
            _thrustLines[Key.Left].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * _torqueballLeftRightThrusterForce, _thrusterOffset_TopLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_TURN;
            _thrustLines[Key.Left].Add(thrustLine);

            // Right
            _thrustLines.Add(Key.Right, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, 1, 0) * _torqueballLeftRightThrusterForce, _thrusterOffset_TopRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_TURN;
            _thrustLines[Key.Right].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(0, -1, 0) * _torqueballLeftRightThrusterForce, _thrusterOffset_BottomLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH_TURN;
            _thrustLines[Key.Right].Add(thrustLine);

            // A
            _thrustLines.Add(Key.A, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(-1, 0, 0) * _thrustForce, _thrusterOffset_TopRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.A].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(-1, 0, 0) * _thrustForce, _thrusterOffset_BottomRight);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.A].Add(thrustLine);

            // D
            _thrustLines.Add(Key.D, new List <ThrustLine>());

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(1, 0, 0) * _thrustForce, _thrusterOffset_TopLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.D].Add(thrustLine);

            thrustLine = new ThrustLine(_map.Viewport, _sharedVisuals, new Vector3D(1, 0, 0) * _thrustForce, _thrusterOffset_BottomLeft);
            thrustLine.LineMaxLength = THRUSTLINELENGTH;
            _thrustLines[Key.D].Add(thrustLine);

            #endregion

            #endregion

            MaterialGroup   material = null;
            GeometryModel3D geometry = null;
            Model3DGroup    models   = new Model3DGroup();
            ModelVisual3D   model    = null;

            #region Interior Extra Visuals

            // These are visuals that will stay oriented to the ship, but don't count in collision calculations

            #region Thrusters

            // These are the little balls, not the thrust lines

            double thrusterLocScale = 1d;

            models.Children.Add(GetThrusterVisual(_thrusterOffset_BottomLeft * thrusterLocScale));
            models.Children.Add(GetThrusterVisual(_thrusterOffset_BottomRight * thrusterLocScale));
            models.Children.Add(GetThrusterVisual(_thrusterOffset_TopLeft * thrusterLocScale));
            models.Children.Add(GetThrusterVisual(_thrusterOffset_TopRight * thrusterLocScale));

            #endregion
            #region Cargo Bay

            //TODO:  Make a visual for this (probably pretty cube like)

            _cargoBay = new CargoBay(_cargoBayMass, _cargoBayVolume);

            _progressBarCargo.Minimum = 0d;
            _progressBarCargo.Maximum = _cargoBay.MaxVolume;
            _progressBarCargo.Value   = 0d;

            #endregion
            #region Fuel Tank

            //TODO:  This visual should be a pill

            _fuelTank                 = new FuelTank();
            _fuelTank.DryMass         = _fuelTankMass;
            _fuelTank.QuantityMax     = _fuelTankCapacity;
            _fuelTank.QuantityCurrent = _fuelTank.QuantityMax;          // a full tank with the purchace of a new ship!!!
            _fuelTank.FuelDensity     = _fuelDensity;

            _progressBarFuel.Minimum = 0d;
            _progressBarFuel.Maximum = _fuelTank.QuantityMax;
            _progressBarFuel.Value   = _fuelTank.QuantityCurrent;

            // Link to the thrusters
            foreach (List <ThrustLine> thrustLines in _thrustLines.Values)
            {
                foreach (ThrustLine thrustLine1 in thrustLines)
                {
                    thrustLine1.FuelToThrustRatio = _fuelThrustRatio;
                    thrustLine1.FuelTank          = _fuelTank;
                }
            }

            #endregion

            #region Core

            // This just looks stupid.  The idea is that you would see the various components (which would also be point masses).  But until
            // the user can modify their ship, it's just an arbitrary ellipse that is ugly

            //// Material
            //material = new MaterialGroup();
            //material.Children.Add(new DiffuseMaterial(Brushes.DimGray));
            //material.Children.Add(new SpecularMaterial(Brushes.DimGray, 100d));

            //// Geometry Model
            //geometry = new GeometryModel3D();
            //geometry.Material = material;
            //geometry.BackMaterial = material;
            //geometry.Geometry = UtilityWPF.GetSphere(5, .45, .25, .05);
            //geometry.Transform = new TranslateTransform3D(0, this.RadiusY * -.25, 0);

            //// Model Visual
            //model = new ModelVisual3D();
            //model.Content = geometry;

            ////NOTE: model.Transform is set to the physics body's transform every frame

            //// Add to the viewport
            //_viewport.Children.Add(model);

            //_visuals.Add(model);

            #endregion

            // Make a model visual for what I have so far
            model         = new ModelVisual3D(); // this is the expensive one, so as few of these should be made as possible
            model.Content = models;

            _visuals.Add(model);
            _map.Viewport.Children.Add(model);

            #endregion

            #region WPF Collision Model

            // Material
            //NOTE:  There seems to be an issue with drawing objects inside a semitransparent object - I think they have to be added in a certain order or something
            //Brush skinBrush = new SolidColorBrush(Color.FromArgb(50, _hullColor.R, _hullColor.G, _hullColor.B));  // making the skin semitransparent, so you can see the components inside
            Brush skinBrush = new SolidColorBrush(_hullColor);  // decided to make it opaque, since I'm not showing anything inside

            material = new MaterialGroup();
            material.Children.Add(new DiffuseMaterial(skinBrush));
            material.Children.Add(new SpecularMaterial(Brushes.White, 75d));     // more reflective (and white light)

            MaterialGroup backMaterial = new MaterialGroup();
            backMaterial.Children.Add(new DiffuseMaterial(skinBrush));
            backMaterial.Children.Add(new SpecularMaterial(new SolidColorBrush(Color.FromArgb(255, 20, 20, 20)), 10d));       // dark light, and not very reflective

            // Geometry Model
            geometry              = new GeometryModel3D();
            geometry.Material     = material;
            geometry.BackMaterial = backMaterial;
            geometry.Geometry     = UtilityWPF.GetSphere_LatLon(5, this.RadiusX, this.RadiusY, this.RadiusZ);

            // Transform
            Transform3DGroup transform = new Transform3DGroup();                // rotate needs to be added before translate
            transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0)));
            transform.Children.Add(new TranslateTransform3D(new Vector3D(0, 0, 0)));

            // Model Visual
            model           = new ModelVisual3D();
            model.Content   = geometry;
            model.Transform = transform;

            _physicsModel = model;    // remember this, so I don't set its transform
            _visuals.Add(model);

            // Add to the viewport (the physics body constructor requires it to be added)
            _map.Viewport.Children.Add(model);

            #endregion
            #region Physics Body

            // Make a physics body that represents this shape
            _physicsBody = new ConvexBody3D(_map.World, model);

            _physicsBody.MaterialGroupID = materialManager.ShipMaterialID;

            _physicsBody.NewtonBody.UserData = this;

            _physicsBody.Mass = Convert.ToSingle(this.TotalMass);

            _physicsBody.LinearDamping = .01f;
            //_physicsBody.AngularDamping = new Vector3D(.01f, .01f, .01f);
            //_physicsBody.AngularDamping = new Vector3D(10f, 10f, 10f);
            _physicsBody.AngularDamping = new Vector3D(1f, 1f, 1f);

            _physicsBody.ApplyForce += new BodyForceEventHandler(Body_ApplyForce);

            #endregion

            #region Exterior Extra Visuals

            // There is a bug in WPF where visuals added after a semitransparent one won't show inside.  The cockpit looks stupid when you can see
            // it inside the skin

            #region Cockpit

            // Material
            material = new MaterialGroup();
            material.Children.Add(new DiffuseMaterial(new SolidColorBrush(UtilityWPF.AlphaBlend(Colors.Teal, Colors.DimGray, .2d))));
            material.Children.Add(new SpecularMaterial(Brushes.White, 100d));

            // Geometry Model
            geometry              = new GeometryModel3D();
            geometry.Material     = material;
            geometry.BackMaterial = material;
            //geometry.Geometry = UtilityWPF.GetSphere(3, .4, .2, .3);
            geometry.Geometry  = UtilityWPF.GetSphere_LatLon(3, .45, .25, .25);
            geometry.Transform = new TranslateTransform3D(0, this.RadiusY * .5, 0);

            // Model Visual
            model         = new ModelVisual3D();
            model.Content = geometry;

            // Add to the viewport
            _visuals.Add(model);
            _map.Viewport.Children.Add(model);

            #endregion
            #region Headlight

            SpotLight spotLight = new SpotLight();
            //spotLight.Color = Color.FromArgb(255, 50, 170, 50);
            spotLight.Color          = UtilityWPF.AlphaBlend(Colors.White, _hullColor, .25d);
            spotLight.Direction      = new Vector3D(0, 1, 0);
            spotLight.OuterConeAngle = 25;
            spotLight.InnerConeAngle = 5;
            //spotLight.LinearAttenuation = .1;
            spotLight.QuadraticAttenuation = .0001;
            spotLight.Range = 1000;

            model         = new ModelVisual3D();
            model.Content = spotLight;

            _visuals.Add(model);
            _map.Viewport.Children.Add(model);

            #endregion

            #endregion

            // Add to the map
            _map.AddItem(this);
        }