public static NQuaternion GetDeltaQuaternionWithDirectionVectors(NVector3 a, NVector3 b) { var dot = NVector3.Dot(a, b); if (dot < -0.999999) { var cross = NVector3.Cross(a, b); if (cross.Length() < 0.000001) { cross = NVector3.Cross(NVector3.UnitY, a); } cross = NVector3.Normalize(cross); return(NQuaternion.CreateFromAxisAngle(cross, Pi)); } else if (dot > 0.999999) { return(new NQuaternion(0, 0, 0, 1)); } else { var xyz = NVector3.Cross(a, b); var w = (float)(Math.Sqrt(a.Length() * a.Length() + b.Length() * b.Length()) + dot); return(new NQuaternion(xyz.X, xyz.Y, xyz.Z, w)); } }
public Plane(Vector3 position, Vector3 normal, JsonMaterial material) { Position = position; Scale = new Vector3(1000f, 1000f, 1000f); this.normal = Vector3.Normalize(normal); this.material = material; }
/// <summary> /// Sets up the joint transforms by automatically creating perpendicular vectors to complete the bases. /// </summary> /// <param name="worldTwistAxisA">Twist axis in world space to attach to entity A.</param> /// <param name="worldTwistAxisB">Twist axis in world space to attach to entity B.</param> public void SetupJointTransforms(System.Numerics.Vector3 worldTwistAxisA, System.Numerics.Vector3 worldTwistAxisB) { worldTwistAxisA.Normalize(); worldTwistAxisB.Normalize(); System.Numerics.Vector3 worldXAxis; Vector3Ex.Cross(ref worldTwistAxisA, ref Toolbox.UpVector, out worldXAxis); float length = worldXAxis.LengthSquared(); if (length < Toolbox.Epsilon) { Vector3Ex.Cross(ref worldTwistAxisA, ref Toolbox.RightVector, out worldXAxis); } worldXAxis.Normalize(); //Complete A's basis. System.Numerics.Vector3 worldYAxis; Vector3Ex.Cross(ref worldTwistAxisA, ref worldXAxis, out worldYAxis); basisA.rotationMatrix = connectionA.orientationMatrix; basisA.SetWorldAxes(worldTwistAxisA, worldXAxis, worldYAxis); //Rotate the axis to B since it could be arbitrarily rotated. System.Numerics.Quaternion rotation; QuaternionEx.GetQuaternionBetweenNormalizedVectors(ref worldTwistAxisA, ref worldTwistAxisB, out rotation); QuaternionEx.Transform(ref worldXAxis, ref rotation, out worldXAxis); basisB.rotationMatrix = connectionB.orientationMatrix; basisB.SetWorldAxes(worldTwistAxisB, worldXAxis); }
public override Vector3 Extrude(Vector3 point) { var center = Body.CenterOfMassPosition; Vector3 vec = point - new Vector3(center.X, center.Y, center.Z); return(Vector3.Normalize(vec) * _radius - vec); }
public Disc(Vector3 position, Vector3 normal, float radius, JsonMaterial material) { Position = position; Scale = new Vector3(radius); this.normal = Vector3.Normalize(normal); this.material = material; }
public override void Redraw(float currentTime, float elapsedTime) { // selected vehicle (user can mouse click to select another) IVehicle selected = Demo.SelectedVehicle; // vehicle nearest mouse (to be highlighted) IVehicle nearMouse = Demo.VehicleNearestToMouse(); // update camera Demo.UpdateCamera(elapsedTime, selected); // draw "ground plane" centered between base and selected vehicle Vector3 goalOffset = Globals.HomeBaseCenter - Demo.Camera.Position; Vector3 goalDirection = Vector3.Normalize(goalOffset); Vector3 cameraForward = Demo.Camera.xxxls().Forward; float goalDot = Vector3.Dot(cameraForward, goalDirection); float blend = Utilities.RemapIntervalClip(goalDot, 1, 0, 0.5f, 0); Vector3 gridCenter = Vector3.Lerp(selected.Position, Globals.HomeBaseCenter, blend); Demo.GridUtility(gridCenter); // draw the seeker, obstacles and home base CtfSeeker.Draw(); DrawObstacles(); DrawHomeBase(); // draw each enemy foreach (CtfEnemy enemy in CtfEnemies) { enemy.Draw(); } // highlight vehicle nearest mouse Demo.HighlightVehicleUtility(nearMouse); }
public XYZ GetNormal(XYZ position) { if (!_WeightedNormals.TryGetValue(position, out XYZ normal)) { normal = position; } return(normal == XYZ.Zero ? XYZ.UnitX : XYZ.Normalize(normal)); }
public void LoadScene(JsonScene scene) { currentScene = scene; editorObjects.Clear(); foreach (JsonGeometry geometry in scene.Geometry) { JsonMaterial material = scene.Materials.SingleOrDefault(m => m.ID == geometry.Material); switch (geometry.Type) { case "plane": { editorObjects.Add(new Plane(geometry.Position, geometry.Normal, material)); break; } case "sphere": { editorObjects.Add(new Sphere(geometry.Position, geometry.Radius, material)); break; } case "disc": { editorObjects.Add(new Disc(geometry.Position, geometry.Normal, geometry.Radius, material)); break; } } } foreach (JsonLight light in scene.Lights) { switch (light.Type) { case "disc": { editorObjects.Add(new Disc(light.Position, light.Normal, light.Radius, new JsonMaterial() { Emission = new JsonEmissionSettings() { Color = light.Color, Brightness = light.Brightness } })); lightPosition = new Vector4(light.Position.X, light.Position.Y, light.Position.Z, light.Radius); lightDirection = Vector3.Normalize(light.Normal); lightColor = new Vector4(light.Color.X, light.Color.Y, light.Color.Z, light.Brightness); break; } } } if (initialized) { editorObjects.ForEach(o => o.Initialize()); } }
/// <summary> /// 获取光线追踪辐照度 /// </summary> /// <param name="ray">光线</param> /// <param name="deep">当前追踪深度</param> /// <returns>颜色,追踪距离</returns> public (Light, float) Light(Ray ray, int deep, RenderObject callerObj, RenderObject ignore = null) { if (deep < 0 || Objects == null || Objects.Count == 0) { return(Core.Light.Dark, 0.0f); } ray = new Ray(ray.Origin, Vector3.Normalize(ray.Direction)); float minDistance = float.NaN; Vector3 point = default, normal = default;
public override RayCastResult Intersection(Ray ray, float nowbest) { { float distance = (Position - ray.Origin).Length(); if (nowbest + R < distance) { return(null); } } Vector3f A = ray.Origin, B = ray.Direction, C = Position; Float a = Vector3f.Dot(B, B); Float b = Vector3f.Dot(B, (A - C)) * 2.0f; Float c = (A - C).LengthSquared() - R * R; float drt = b * b - 4 * a * c; if (drt < 0) { return(null); } drt = Math.Sqrt(drt); float x1 = (-b + drt) / a / 2; float x2 = (-b - drt) / a / 2; if (x1 < 0 && x2 < 0) { return(null); } float d; if (x1 > 0 && x2 > 0) { d = Math.Max(x1, x2); } else if (x1 > 0) { d = x1; } else { d = x2; } RayCastResult result = new RayCastResult(); //result.happened = true; result.obj = this; result.material = Material; result.coords = ray.Origin + ray.Direction * d; result.distance = d; result.normal = Vector3f.Normalize(result.coords - Position); return(result); }
private void UpdateRestrictedAxes() { localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Up, localAxisA); if (localConstrainedAxis1.LengthSquared() < .001f) { localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Right, localAxisA); } localConstrainedAxis2 = System.Numerics.Vector3.Cross(localAxisA, localConstrainedAxis1); localConstrainedAxis1.Normalize(); localConstrainedAxis2.Normalize(); }
// draws a "wide line segment": a rectangle of the given width and color // whose mid-line connects two given endpoints public static void DrawXZWideLine(Vector3 startPoint, Vector3 endPoint, Color color, float width) { Vector3 offset = Vector3.Normalize(endPoint - startPoint); Vector3 perp = _localSpace.LocalRotateForwardToSide(offset); Vector3 radius = perp * width / 2; Vector3 a = startPoint + radius; Vector3 b = endPoint + radius; Vector3 c = endPoint - radius; Vector3 d = startPoint - radius; iDrawQuadrangle(a, b, c, d, color); }
private void FireMissile(Fighter launcher, Fighter target) { if (_missiles.Count(m => m.Target == target) < 3) { _missiles.Add(new Missile(_pd, target, Annotations) { Position = launcher.Position, Forward = Vector3.Normalize(launcher.Forward * 0.9f + Vector3Helpers.RandomUnitVector() * 0.1f), Speed = launcher.Speed, Color = _team1.Contains(launcher) ? Color.Black : Color.White }); } }
//--------------------------------OTHERS----------------------------------------// /** * Computes the distance from the line point to another point * * @param otherPoint the point to compute the distance from the line point. The point * is supposed to be on the same line. * @return points distance. If the point submitted is behind the direction, the * distance is negative */ public double computePointToPointDistance(Point3d otherPoint) { //float distance = otherPoint.distance(point); float distance = Vector3d.Distance(otherPoint, point); Vector3d vec = Vector3d.Normalize(new Vector3d(otherPoint.X - point.X, otherPoint.Y - point.Y, otherPoint.Z - point.Z)); if (Vector3d.Dot(vec, direction) < 0) { return(-distance); } else { return(distance); } }
/// <summary> /// 返回折射光方向和能量强度 /// </summary> /// <param name="dir"></param> /// <param name="niOverNt"></param> /// <returns></returns> public static (float, Vector3) Refract(Vector3 dir, Vector3 normal, float niOverNt) { dir = -Vector3.Normalize(dir); float cosAlpha = Vector3.Dot(dir, normal); float discriminant = 1.0f - niOverNt * niOverNt * (1.0f - cosAlpha * cosAlpha); if (discriminant > 0) { float cosGamma = MathF.Sqrt(discriminant); Vector3 re = ((normal * cosAlpha) - dir) * niOverNt - normal * cosGamma; return(1.0f - Schlick(cosAlpha, niOverNt), re); } else { return(float.NegativeInfinity, Vector3.Zero); } }
/** * Gets the face normal * * @return face normal */ public Vector3d getNormal() { Point3d p1 = v1.getPosition(); Point3d p2 = v2.getPosition(); Point3d p3 = v3.getPosition(); Vector3d xy, xz, normal; xy = new Vector3d(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z); xz = new Vector3d(p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z); /*normal = new Vector3d(); * normal.cross(xy, xz); * normal.normalize();*/ normal = Vector3d.Normalize(Vector3d.Cross(xy, xz)); return(normal); }
private static IFlowField GenerateFlowField() { var f = new SimpleFlowField(50, 1, 50, new Vector3(25, 0.5f, 25)); //Start random f.Randomize(1); //Swirl around center //Half the field is a swirl (basically just concentric circles) while the other half has a slight bias to spiral inwards towards the center f.Func(pos => Vector3.Lerp(pos / 5, Vector3.Normalize(Vector3.Cross(pos, Vector3.UnitY)), pos.X > 0.5f ? 0.75f : 0.9f), 0.85f); //Keep it flat on the plane f.ClampXZ(); //Clean NaN values f.Clean(); return(f); }
//----------------------------------CONSTRUCTORS---------------------------------// /** * Constructor for a line. The line created is the intersection between two planes * * @param face1 face representing one of the planes * @param face2 face representing one of the planes */ public Line(Face face1, Face face2) { Vector3d normalFace1 = face1.getNormal(); Vector3d normalFace2 = face2.getNormal(); //direction: cross product of the faces normals //direction = new Vector3d(); //direction.cross(normalFace1, normalFace2); direction = Vector3d.Cross(normalFace1, normalFace2); //if direction lenght is not zero (the planes aren't parallel )... if (!(direction.Length() < TOL)) { //getting a line point, zero is set to a coordinate whose direction //component isn't zero (line intersecting its origin plan) point = new Point3d(); float d1 = -(normalFace1.X * face1.v1.X + normalFace1.Y * face1.v1.Y + normalFace1.Z * face1.v1.Z); float d2 = -(normalFace2.X * face2.v1.X + normalFace2.Y * face2.v1.Y + normalFace2.Z * face2.v1.Z); if (Math.Abs(direction.X) > TOL) { point.X = 0; point.Y = (d2 * normalFace1.Z - d1 * normalFace2.Z) / direction.X; point.Z = (d1 * normalFace2.Y - d2 * normalFace1.Y) / direction.X; } else if (Math.Abs(direction.Y) > TOL) { point.X = (d1 * normalFace2.Z - d2 * normalFace1.Z) / direction.Y; point.Y = 0; point.Z = (d2 * normalFace1.X - d1 * normalFace2.X) / direction.Y; } else { point.X = (d2 * normalFace1.Y - d1 * normalFace2.Y) / direction.Z; point.Y = (d1 * normalFace2.X - d2 * normalFace1.X) / direction.Z; point.Z = 0; } } direction = Vector3d.Normalize(direction); }
private readonly void DrawTriangle(int lod, XYZ a, XYZ b, XYZ c) { a = XYZ.Normalize(a); b = XYZ.Normalize(b); c = XYZ.Normalize(c); if (lod <= 0) { Span <Point3> abc = stackalloc Point3[3]; if (_FaceFlip) { abc[2] = _Center + a * _Radius; abc[1] = _Center + b * _Radius; abc[0] = _Center + c * _Radius; } else { abc[0] = _Center + a * _Radius; abc[1] = _Center + b * _Radius; abc[2] = _Center + c * _Radius; } _Context.DrawConvexSurface(abc, _Color); return; } --lod; var ab = (a + b) * 0.5f; var bc = (b + c) * 0.5f; var ca = (c + a) * 0.5f; DrawTriangle(lod, a, ab, ca); DrawTriangle(lod, ab, b, bc); DrawTriangle(lod, bc, c, ca); DrawTriangle(lod, ab, bc, ca); }
// called when steerToFollowPath decides steering is required public void AnnotatePathFollowing(Vector3 future, Vector3 onPath, Vector3 target, float outside) { Color yellow = Color.Yellow; Color lightOrange = new Color((byte)(255.0f * 1.0f), (byte)(255.0f * 0.5f), 0); Color darkOrange = new Color((byte)(255.0f * 0.6f), (byte)(255.0f * 0.3f), 0); // draw line from our position to our predicted future position annotation.Line(Position, future, yellow.ToVector3().FromXna()); // draw line from our position to our steering target on the path annotation.Line(Position, target, Color.Orange.ToVector3().FromXna()); // draw a two-toned line between the future test point and its // projection onto the path, the change from dark to light color // indicates the boundary of the tube. Vector3 boundaryOffset = Vector3.Normalize(onPath - future); boundaryOffset *= outside; Vector3 onPathBoundary = future + boundaryOffset; annotation.Line(onPath, onPathBoundary, darkOrange.ToVector3().FromXna()); annotation.Line(onPathBoundary, future, lightOrange.ToVector3().FromXna()); }
static TrigleFace[] Smooth(List <PW_Trigle> faces, List <Vector3f> v, List <Vector2f> vt, Dictionary <int, List <PW_Trigle> > faceList) { if (faces == null || faces.Count == 0) { return(null); } Dictionary <int, Vector3f> pointNormal = new Dictionary <int, Vector3f>(); TrigleFace[] re = new TrigleFace[faces.Count]; foreach (KeyValuePair <int, List <PW_Trigle> > kv in faceList) { Vector3f n = new Vector3f(); foreach (PW_Trigle trigle in kv.Value) { n += trigle.Normal; } n = Vector3f.Normalize(n); pointNormal[kv.Key] = n; } int idx = 0; foreach (PW_Trigle trigle in faces) { TrigleFace t = new TrigleFace(v[trigle.v0], v[trigle.v1], v[trigle.v2]); t.sp0 = vt[trigle.vt0]; t.sp1 = vt[trigle.vt1]; t.sp2 = vt[trigle.vt2]; t.n0 = pointNormal[trigle.v0]; t.n1 = pointNormal[trigle.v1]; t.n2 = pointNormal[trigle.v2]; re[idx] = t; idx++; } return(re); }
virtual public Transform Step(double time, LocationOptions options) { Clamp(options); var up = new Vector3(normal.x, normal.y, -normal.z); Vector3 east = new Vector3(local_orientation.v11, local_orientation.v21, local_orientation.v31);; Vector3 north; if (options.RotationOptions.HasFlag(RotationOptions.AlignToSurface)) { east = Vector3.Normalize(east - (Vector3.Dot(east, up) * up)); north = Vector3.Cross(east, up); } else { north = new Vector3(-local_orientation.v12, -local_orientation.v22, -local_orientation.v32); } var m = new Matrix4x4(east.X, east.Y, east.Z, 0, up.X, up.Y, up.Z, 0, north.X, north.Y, north.Z, 0, 0, 0, 0, 1); var qm = Quaternion.CreateFromRotationMatrix(m); var r = qm * Rotation; return(new Transform { Pos = { X = (float)position.x, Y = (float)position.y, Z = (float)position.z }, Rot = r }); }
public void AxisAngle_ToQuaternionConversion() { AxisAngle aa; Quaternion q; Vector v; SysVec normV; SysQuat sq; double x, y, z, angle; bool zero; // Test random quaternions for (var i = 0; i < 50; i++) { x = Random(-100, 100); y = Random(-100, 100); z = Random(-100, 100); angle = Random(-720, 720); Trace.WriteLine(""); Trace.WriteLine(x + " " + y + " " + z + " " + angle + " length: " + Geometry.Length(x, y, z)); aa = new AxisAngle(x, y, z, angle); q = aa.ToQuaternion(); // this method will normalize the Quaternion, as neccesary for spatial rotation representation Trace.WriteLine(aa); Trace.WriteLine(q); // TEST 1: compare to System.Numeric.Quaternion normV = new SysVec((float)x, (float)y, (float)z); normV = SysVec.Normalize(normV); sq = SysQuat.CreateFromAxisAngle(normV, (float)(angle * Math.PI / 180.0)); // now this Quaternion SHOULD be normalized... Trace.WriteLine(sq + " length: " + sq.Length()); Assert.AreEqual(q.W, sq.W, 0.000001, "Failed W"); // can't go very precise due to float imprecision in sys quat Assert.AreEqual(q.X, sq.X, 0.000001, "Failed X"); Assert.AreEqual(q.Y, sq.Y, 0.000001, "Failed Y"); Assert.AreEqual(q.Z, sq.Z, 0.000001, "Failed Z"); } // Test all permutations of unitary components quaternions (including zero) for (x = -1; x <= 1; x++) { for (y = -1; y <= 1; y++) { for (z = -1; z <= 1; z++) { for (angle = -720; angle <= 720; angle += 22.5) { Trace.WriteLine(""); Trace.WriteLine(x + " " + y + " " + z + " " + angle + " length: " + Geometry.Length(x, y, z)); // Normalize v = new Vector(x, y, z); v.Normalize(); aa = new AxisAngle(v, angle); q = aa.ToQuaternion(); Trace.WriteLine(aa); Trace.WriteLine(q); zero = aa.IsZero(); if (zero) { Assert.IsTrue(new Quaternion(1, 0, 0, 0).IsSimilar(q), "Failed zero quaternion"); } else { // TEST 1: compare to System.Numeric.Quaternion //sq = SysQuat.CreateFromAxisAngle(new Vector3((float)x, (float)y, (float)z), (float)(angle * Math.PI / 180.0)); // this Quaternion is not a versor (not unit) normV = new SysVec((float)x, (float)y, (float)z); normV = SysVec.Normalize(normV); sq = SysQuat.CreateFromAxisAngle(normV, (float)(angle * Math.PI / 180.0)); // now this Quaternion SHOULD be normalized... Trace.WriteLine(sq + " length: " + sq.Length()); Assert.AreEqual(q.W, sq.W, 0.000001, "Failed W"); // can't go very precise due to float imprecision in sys quat Assert.AreEqual(q.X, sq.X, 0.000001, "Failed X"); Assert.AreEqual(q.Y, sq.Y, 0.000001, "Failed Y"); Assert.AreEqual(q.Z, sq.Z, 0.000001, "Failed Z"); } } } } } }
internal void PreStep(float dt) { vehicleEntity = wheel.Vehicle.Body; supportEntity = wheel.SupportingEntity; supportIsDynamic = supportEntity != null && supportEntity.isDynamic; Vector3Ex.Cross(ref wheel.normal, ref wheel.slidingFriction.slidingFrictionAxis, out forceAxis); forceAxis.Normalize(); //Do not need to check for normalize safety because normal and sliding friction axis must be perpendicular. linearAX = forceAxis.X; linearAY = forceAxis.Y; linearAZ = forceAxis.Z; //angular A = Ra x N angularAX = (wheel.ra.Y * linearAZ) - (wheel.ra.Z * linearAY); angularAY = (wheel.ra.Z * linearAX) - (wheel.ra.X * linearAZ); angularAZ = (wheel.ra.X * linearAY) - (wheel.ra.Y * linearAX); //Angular B = N x Rb angularBX = (linearAY * wheel.rb.Z) - (linearAZ * wheel.rb.Y); angularBY = (linearAZ * wheel.rb.X) - (linearAX * wheel.rb.Z); angularBZ = (linearAX * wheel.rb.Y) - (linearAY * wheel.rb.X); //Compute inverse effective mass matrix float entryA, entryB; //these are the transformed coordinates float tX, tY, tZ; if (vehicleEntity.isDynamic) { tX = angularAX * vehicleEntity.inertiaTensorInverse.M11 + angularAY * vehicleEntity.inertiaTensorInverse.M21 + angularAZ * vehicleEntity.inertiaTensorInverse.M31; tY = angularAX * vehicleEntity.inertiaTensorInverse.M12 + angularAY * vehicleEntity.inertiaTensorInverse.M22 + angularAZ * vehicleEntity.inertiaTensorInverse.M32; tZ = angularAX * vehicleEntity.inertiaTensorInverse.M13 + angularAY * vehicleEntity.inertiaTensorInverse.M23 + angularAZ * vehicleEntity.inertiaTensorInverse.M33; entryA = tX * angularAX + tY * angularAY + tZ * angularAZ + vehicleEntity.inverseMass; } else { entryA = 0; } if (supportIsDynamic) { tX = angularBX * supportEntity.inertiaTensorInverse.M11 + angularBY * supportEntity.inertiaTensorInverse.M21 + angularBZ * supportEntity.inertiaTensorInverse.M31; tY = angularBX * supportEntity.inertiaTensorInverse.M12 + angularBY * supportEntity.inertiaTensorInverse.M22 + angularBZ * supportEntity.inertiaTensorInverse.M32; tZ = angularBX * supportEntity.inertiaTensorInverse.M13 + angularBY * supportEntity.inertiaTensorInverse.M23 + angularBZ * supportEntity.inertiaTensorInverse.M33; entryB = tX * angularBX + tY * angularBY + tZ * angularBZ + supportEntity.inverseMass; } else { entryB = 0; } velocityToImpulse = -1 / (entryA + entryB); //Softness? currentFrictionCoefficient = gripFrictionBlender(gripFriction, wheel.supportMaterial.kineticFriction, true, wheel); //Compute the maximum force if (targetSpeed > 0) { maxMotorForceDt = maximumForwardForce * dt; } else { maxMotorForceDt = -maximumBackwardForce * dt; } }
internal void PreStep(float dt) { vehicleEntity = wheel.Vehicle.Body; supportEntity = wheel.SupportingEntity; supportIsDynamic = supportEntity != null && supportEntity.isDynamic; Vector3Ex.Cross(ref wheel.worldForwardDirection, ref wheel.normal, out slidingFrictionAxis); float axisLength = slidingFrictionAxis.LengthSquared(); //Safety against bad cross product if (axisLength < Toolbox.BigEpsilon) { Vector3Ex.Cross(ref wheel.worldForwardDirection, ref Toolbox.UpVector, out slidingFrictionAxis); axisLength = slidingFrictionAxis.LengthSquared(); if (axisLength < Toolbox.BigEpsilon) { Vector3Ex.Cross(ref wheel.worldForwardDirection, ref Toolbox.RightVector, out slidingFrictionAxis); } } slidingFrictionAxis.Normalize(); linearAX = slidingFrictionAxis.X; linearAY = slidingFrictionAxis.Y; linearAZ = slidingFrictionAxis.Z; //angular A = Ra x N angularAX = (wheel.ra.Y * linearAZ) - (wheel.ra.Z * linearAY); angularAY = (wheel.ra.Z * linearAX) - (wheel.ra.X * linearAZ); angularAZ = (wheel.ra.X * linearAY) - (wheel.ra.Y * linearAX); //Angular B = N x Rb angularBX = (linearAY * wheel.rb.Z) - (linearAZ * wheel.rb.Y); angularBY = (linearAZ * wheel.rb.X) - (linearAX * wheel.rb.Z); angularBZ = (linearAX * wheel.rb.Y) - (linearAY * wheel.rb.X); //Compute inverse effective mass matrix float entryA, entryB; //these are the transformed coordinates float tX, tY, tZ; if (vehicleEntity.isDynamic) { tX = angularAX * vehicleEntity.inertiaTensorInverse.M11 + angularAY * vehicleEntity.inertiaTensorInverse.M21 + angularAZ * vehicleEntity.inertiaTensorInverse.M31; tY = angularAX * vehicleEntity.inertiaTensorInverse.M12 + angularAY * vehicleEntity.inertiaTensorInverse.M22 + angularAZ * vehicleEntity.inertiaTensorInverse.M32; tZ = angularAX * vehicleEntity.inertiaTensorInverse.M13 + angularAY * vehicleEntity.inertiaTensorInverse.M23 + angularAZ * vehicleEntity.inertiaTensorInverse.M33; entryA = tX * angularAX + tY * angularAY + tZ * angularAZ + vehicleEntity.inverseMass; } else { entryA = 0; } if (supportIsDynamic) { tX = angularBX * supportEntity.inertiaTensorInverse.M11 + angularBY * supportEntity.inertiaTensorInverse.M21 + angularBZ * supportEntity.inertiaTensorInverse.M31; tY = angularBX * supportEntity.inertiaTensorInverse.M12 + angularBY * supportEntity.inertiaTensorInverse.M22 + angularBZ * supportEntity.inertiaTensorInverse.M32; tZ = angularBX * supportEntity.inertiaTensorInverse.M13 + angularBY * supportEntity.inertiaTensorInverse.M23 + angularBZ * supportEntity.inertiaTensorInverse.M33; entryB = tX * angularBX + tY * angularBY + tZ * angularBZ + supportEntity.inverseMass; } else { entryB = 0; } velocityToImpulse = -1 / (entryA + entryB); //Softness? //Compute friction. //Which coefficient? Check velocity. if (Math.Abs(RelativeVelocity) < staticFrictionVelocityThreshold) { blendedCoefficient = frictionBlender(staticCoefficient, wheel.supportMaterial.staticFriction, false, wheel); } else { blendedCoefficient = frictionBlender(kineticCoefficient, wheel.supportMaterial.kineticFriction, true, wheel); } }
private void UpdateRestrictedAxes() { localRestrictedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Up, localLineDirection); if (localRestrictedAxis1.LengthSquared() < .001f) { localRestrictedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Right, localLineDirection); } localRestrictedAxis2 = System.Numerics.Vector3.Cross(localLineDirection, localRestrictedAxis1); localRestrictedAxis1.Normalize(); localRestrictedAxis2.Normalize(); }
bool TryToStepUsingContact(ref ContactData contact, ref System.Numerics.Vector3 down, out System.Numerics.Vector3 newPosition) { System.Numerics.Vector3 position = characterBody.Position; //The normal of the contact may not be facing perfectly out to the side. //The detection process allows a bit of slop. //Correct it by removing any component of the normal along the local up vector. System.Numerics.Vector3 normal = contact.Normal; float dot; Vector3Ex.Dot(ref normal, ref down, out dot); System.Numerics.Vector3 error; Vector3Ex.Multiply(ref down, dot, out error); Vector3Ex.Subtract(ref normal, ref error, out normal); normal.Normalize(); //Now we need to ray cast out from the center of the character in the direction of this normal to check for obstructions. //Compute the ray origin location. Fire it out of the top of the character; if we're stepping, this must be a valid location. //Putting it as high as possible helps to reject more invalid step geometry. Ray ray; float downRayLength = characterBody.Height;// MaximumStepHeight + upStepMargin; Vector3Ex.Multiply(ref down, characterBody.Height * .5f - downRayLength, out ray.Position); Vector3Ex.Add(ref ray.Position, ref position, out ray.Position); ray.Direction = normal; //Include a little margin in the length. //Technically, the character only needs to teleport horizontally by the complicated commented expression. //That puts it just far enough to have traction on the new surface. //In practice, the current contact refreshing approach used for many pair types causes contacts to persist horizontally a bit, //which can cause side effects for the character. float horizontalOffsetAmount = characterBody.CollisionInformation.Shape.CollisionMargin; // (float)((1 - character.SupportFinder.sinMaximumSlope) * character.Body.CollisionInformation.Shape.CollisionMargin + 0); float length = characterBody.Radius + horizontalOffsetAmount; // -contact.PenetrationDepth; if (QueryManager.RayCastHitAnything(ray, length)) { //The step is obstructed! newPosition = new System.Numerics.Vector3(); return(false); } //The down-cast ray origin has been verified by the previous ray cast. //Let's look for a support! System.Numerics.Vector3 horizontalOffset; Vector3Ex.Multiply(ref normal, length, out horizontalOffset); Vector3Ex.Add(ref ray.Position, ref horizontalOffset, out ray.Position); ray.Direction = down; //Find the earliest hit, if any. RayHit earliestHit; if (!QueryManager.RayCast(ray, downRayLength, out earliestHit) || //Can't do anything if it didn't hit. earliestHit.T <= 0 || //Can't do anything if the hit was invalid. earliestHit.T - downRayLength > -minimumUpStepHeight || //Don't bother doing anything if the step is too small. earliestHit.T - downRayLength < -maximumStepHeight - upStepMargin) //Can't do anything if the step is too tall. { //No valid hit was detected. newPosition = new System.Numerics.Vector3(); return(false); } //Ensure the candidate surface supports traction. System.Numerics.Vector3 supportNormal; Vector3Ex.Normalize(ref earliestHit.Normal, out supportNormal); //Calibrate the normal to face in the same direction as the down vector for consistency. Vector3Ex.Dot(ref supportNormal, ref down, out dot); if (dot < 0) { Vector3Ex.Negate(ref supportNormal, out supportNormal); dot = -dot; } //If the new surface does not have traction, do not attempt to step up. if (dot < ContactCategorizer.TractionThreshold) { newPosition = new System.Numerics.Vector3(); return(false); } //Since contact queries are frequently expensive compared to ray cast tests, //do one more ray cast test. This time, starting from the same position, cast upwards. //In order to step up, the previous down-ray hit must be at least a character height away from the result of the up-ray. Vector3Ex.Negate(ref down, out ray.Direction); //Find the earliest hit, if any. //RayHit earliestHitUp = new RayHit(); //earliestHitUp.T = float.MaxValue; float upLength = characterBody.Height - earliestHit.T; //If the sum of the up and down distances is less than the height, the character can't fit. if (QueryManager.RayCastHitAnything(ray, upLength)) { newPosition = new System.Numerics.Vector3(); return(false); } //By now, a valid ray hit has been found. Now we need to validate it using contact queries. //This process is very similar in concept to the down step verification, but it has some extra //requirements. //Predict a hit location based on the time of impact and the normal at the intersection. //Take into account the radius of the character (don't forget the collision margin!) RigidTransform transform = characterBody.CollisionInformation.WorldTransform; //The transform must be modified to position the query body at the right location. //The horizontal offset of the queries ensures that a tractionable part of the character will be put onto the new support. Vector3Ex.Multiply(ref normal, horizontalOffsetAmount, out horizontalOffset); Vector3Ex.Add(ref transform.Position, ref horizontalOffset, out transform.Position); System.Numerics.Vector3 verticalOffset; Vector3Ex.Multiply(ref down, -downRayLength, out verticalOffset); Vector3Ex.Add(ref transform.Position, ref verticalOffset, out transform.Position); //We know that the closest point to the plane will be the extreme point in the plane's direction. //Use it as the ray origin. Ray downRay; characterBody.CollisionInformation.Shape.GetExtremePoint(supportNormal, ref transform, out downRay.Position); downRay.Direction = down; //Intersect the ray against the plane defined by the support hit. System.Numerics.Vector3 intersection; Vector3Ex.Dot(ref earliestHit.Location, ref supportNormal, out dot); Plane plane = new Plane(supportNormal, dot); System.Numerics.Vector3 candidatePosition; //Define the interval bounds to be used later. //The words 'highest' and 'lowest' here refer to the position relative to the character's body. //The ray cast points downward relative to the character's body. float highestBound = -maximumStepHeight; float lowestBound = characterBody.CollisionInformation.Shape.CollisionMargin - downRayLength + earliestHit.T; float currentOffset = lowestBound; float hintOffset; var tractionContacts = new QuickList <CharacterContact>(BufferPools <CharacterContact> .Thread); var supportContacts = new QuickList <CharacterContact>(BufferPools <CharacterContact> .Thread); var sideContacts = new QuickList <CharacterContact>(BufferPools <CharacterContact> .Thread); var headContacts = new QuickList <CharacterContact>(BufferPools <CharacterContact> .Thread); try { //This guess may either win immediately, or at least give us a better idea of where to search. float hitT; if (Toolbox.GetRayPlaneIntersection(ref downRay, ref plane, out hitT, out intersection)) { hitT = -downRayLength + hitT + CollisionDetectionSettings.AllowedPenetration; if (hitT < highestBound) { //Don't try a location known to be too high. hitT = highestBound; } currentOffset = hitT; if (currentOffset > lowestBound) { lowestBound = currentOffset; } candidatePosition = characterBody.Position + down * currentOffset + horizontalOffset; switch (TryUpStepPosition(ref normal, ref candidatePosition, ref down, ref tractionContacts, ref supportContacts, ref sideContacts, ref headContacts, out hintOffset)) { case CharacterContactPositionState.Accepted: currentOffset += hintOffset; //Only use the new position location if the movement distance was the right size. if (currentOffset < 0 && currentOffset > -maximumStepHeight - CollisionDetectionSettings.AllowedPenetration) { //It's possible that we let a just-barely-too-high step occur, limited by the allowed penetration. //Just clamp the overall motion and let it penetrate a bit. newPosition = characterBody.Position + Math.Max(-maximumStepHeight, currentOffset) * down + horizontalOffset; return(true); } else { newPosition = new System.Numerics.Vector3(); return(false); } case CharacterContactPositionState.Rejected: newPosition = new System.Numerics.Vector3(); return(false); case CharacterContactPositionState.NoHit: highestBound = currentOffset + hintOffset; currentOffset = (lowestBound + currentOffset) * .5f; break; case CharacterContactPositionState.Obstructed: lowestBound = currentOffset; currentOffset = (highestBound + currentOffset) * .5f; break; case CharacterContactPositionState.HeadObstructed: highestBound = currentOffset + hintOffset; currentOffset = (lowestBound + currentOffset) * .5f; break; case CharacterContactPositionState.TooDeep: currentOffset += hintOffset; lowestBound = currentOffset; break; } } //TODO: If the ray cast doesn't hit, that could be used to early out... Then again, it pretty much can't happen. //Our guesses failed. //Begin the regular process. Start at the time of impact of the ray itself. //How about trying the time of impact of the ray itself? //Since we wouldn't be here unless there were no contacts at the body's current position, //testing the ray cast location gives us the second bound we need to do an informed binary search. int attempts = 0; //Don't keep querying indefinitely. If we fail to reach it in a few informed steps, it's probably not worth continuing. //The bound size check prevents the system from continuing to search a meaninglessly tiny interval. while (attempts++ < 5 && lowestBound - highestBound > Toolbox.BigEpsilon) { candidatePosition = characterBody.Position + currentOffset * down + horizontalOffset; switch (TryUpStepPosition(ref normal, ref candidatePosition, ref down, ref tractionContacts, ref supportContacts, ref sideContacts, ref headContacts, out hintOffset)) { case CharacterContactPositionState.Accepted: currentOffset += hintOffset; //Only use the new position location if the movement distance was the right size. if (currentOffset < 0 && currentOffset > -maximumStepHeight - CollisionDetectionSettings.AllowedPenetration) { //It's possible that we let a just-barely-too-high step occur, limited by the allowed penetration. //Just clamp the overall motion and let it penetrate a bit. newPosition = characterBody.Position + Math.Max(-maximumStepHeight, currentOffset) * down + horizontalOffset; return(true); } else { newPosition = new System.Numerics.Vector3(); return(false); } case CharacterContactPositionState.Rejected: newPosition = new System.Numerics.Vector3(); return(false); case CharacterContactPositionState.NoHit: highestBound = currentOffset + hintOffset; currentOffset = (lowestBound + highestBound) * .5f; break; case CharacterContactPositionState.Obstructed: lowestBound = currentOffset; currentOffset = (highestBound + lowestBound) * .5f; break; case CharacterContactPositionState.HeadObstructed: highestBound = currentOffset + hintOffset; currentOffset = (lowestBound + currentOffset) * .5f; break; case CharacterContactPositionState.TooDeep: currentOffset += hintOffset; lowestBound = currentOffset; break; } } } finally { tractionContacts.Dispose(); supportContacts.Dispose(); sideContacts.Dispose(); headContacts.Dispose(); } //Couldn't find a candidate. newPosition = new System.Numerics.Vector3(); return(false); }
/// <summary> /// 相交点辐射光 /// </summary> /// <param name="point"></param> /// <param name="normal"></param> /// <param name="deep"></param> /// <returns></returns> public virtual Light IntersectLight(Vector3 point, Vector3 dir, Vector3 normal, int deep) { #if RayDebugger SceneDebug Debugger = Scene.debugger; if (Debugger != null) { Debugger.BeginBranch(point); } #endif Light returnlight = default; // 发光体返回发光颜色 if (Material.LightAble) { returnlight = Material.LightColor; goto returnPoint; } // 递归深度极限 if (deep <= 1) { returnlight = Material.BaseColor * 0.3f; goto returnPoint; } dir = Vector3.Normalize(dir); bool IsBackFace = false; if (Vector3.Dot(dir, normal) > 0) // 背面 { IsBackFace = true; normal = -normal; } #region 计算追踪光线总数 int traceRayNum = RenderConfiguration.Configurations.ReflectSmapingLevel - RenderConfiguration.Configurations.RayTraceDeep + deep; { traceRayNum = (int)(traceRayNum * Material.AMetalDegree); if (traceRayNum < 1) { traceRayNum = 1; } traceRayNum = traceRayNum * 3 - 2; } #endregion #region 计算折射光 Light refractl = default; // 折射光 float refractPower = 0.0f; // 折射光强度 if (Material.IsTransparent) { float riindex = Material.RefractiveIndices; if (!IsBackFace) { riindex = 1.0f / riindex; } // 计算折射光线 (float pow, Vector3 rdir) = Tools.Refract(dir, normal, riindex); if (pow < 0) { goto endRefract; } refractPower = pow * Material.TransparentIndex; int raycount = (int)(traceRayNum * refractPower); traceRayNum -= raycount; float randomScale = Material.AMetalDegree * Material.AMetalDegree * 0.5f; //raycount = (int)(traceRayNum * randomScale); //raycount = (int)(traceRayNum * Material.AMetalDegree); if (raycount < 1 && refractPower > 0.00001) { raycount = 1; } if (raycount == 0) { refractl = Material.BaseColor; goto endRefract; } rdir = normal * randomScale + rdir * (1.0f - randomScale); for (int nsmap = 0; nsmap < raycount; nsmap++) { Vector3 raydir = Tools.RandomPointInSphere() * randomScale + rdir; Ray r = new Ray(point, raydir); //Console.WriteLine('\t' + this.Name + " [refract] : " + r); (Light c, float distance) = Scene.Light(r, deep - 1, this); if (IsBackFace) //内部光线,进行吸收计算 { float xsl = Math.Log(distance + 1.0f) + 1.0f; refractl *= Material.BaseColor / xsl; } refractl += c; } refractl /= raycount; } endRefract: #endregion #region 计算反射光 Light reflectl = default; // 反射光 { int raycount = traceRayNum; if (raycount < 1) { if (refractPower < 0.99f) { raycount = 1; } else { reflectl = Material.BaseColor; goto endReflact; } } Vector3 spO; { //Vector3 spRO = Tools.Reflect(dir, normal); Vector3 spRO = Vector3.Reflect(dir, normal); //spO = normal * (1.0f - Material.MetalDegree) + spRO * Material.MetalDegree; spO = Vector3.Lerp(normal, spRO, Material.MetalDegree); } for (int nsmap = 0; nsmap < raycount; nsmap++) { Vector3 tp = Tools.RandomPointInSphere() * Material.AMetalDegree + spO; Vector3 raydir = tp; while (raydir.LengthSquared() < 0.1) { tp = Tools.RandomPointInSphere() + spO; raydir = tp; } Ray r = new Ray(point, raydir); //Console.WriteLine('\t' + this.Name + " [reflact] : " + r); (Light c, float _) = Scene.Light(r, deep - 1, this); //, this); reflectl += c; } reflectl /= raycount; reflectl *= (0.06f * Material.MetalDegree + 0.93f) * Material.BaseColor; } #endregion returnlight = refractl * refractPower + reflectl * (1.0f - refractPower); endReflact: returnPoint: #if RayDebugger if (Debugger != null) { Debugger.EndBranch(); } #endif return(returnlight); }
public ImportedFLVER2Model ImportFromAssimpScene(Scene scene, FLVER2ImportSettings settings) { LoadMaterialInfoBankForGame(settings.Game); var result = new ImportedFLVER2Model(); var flver = result.Flver = new FLVER2(); flver.Header.BigEndian = settings.FlverHeader.BigEndian; flver.Header.BoundingBoxMax = new NVector3(float.MinValue); flver.Header.BoundingBoxMin = new NVector3(float.MaxValue); flver.Header.Unicode = settings.FlverHeader.Unicode; flver.Header.Unk4A = settings.FlverHeader.Unk4A; flver.Header.Unk4C = settings.FlverHeader.Unk4C; flver.Header.Unk5C = settings.FlverHeader.Unk5C; flver.Header.Unk5D = settings.FlverHeader.Unk5D; flver.Header.Unk68 = settings.FlverHeader.Unk68; flver.Header.Version = settings.FlverHeader.Version; var flverSceneMatrix = NMatrix.CreateScale(NVector3.One * settings.SceneScale); if (settings.ConvertFromZUp) { flverSceneMatrix *= SapMath.ZUpToYUpNMatrix; } //flverSceneMatrix *= NMatrix.CreateRotationY(SapMath.Pi); flverSceneMatrix *= settings.SceneCorrectMatrix; flverSceneMatrix *= NMatrix.CreateScale(1, 1, -1); var coordMat = AssimpUtilities.GetSceneCoordSystemMatrix(scene); scene.RootNode.Transform *= coordMat; var skeletonRootNode = AssimpUtilities.FindRootNode(scene, settings.RootNodeName, out Matrix4x4 skeletonRootNodeMatrix); var metaskeleton = FLVERImportHelpers.GenerateFlverMetaskeletonFromRootNode( skeletonRootNode, skeletonRootNodeMatrix, settings.SceneScale); flver.Bones = metaskeleton.Bones; flver.Dummies = metaskeleton.DummyPoly; foreach (var b in flver.Bones) { // Mark as dummied-out bone until iterating over them later and seeing which are weighted to meshes. if (b.ParentIndex == -1) { b.Unk3C = 1; } } var usesIndirectBones = flver.Header.Version <= 0x20010; if (settings.SkeletonTransformsOverride != null) { flver.Bones = settings.SkeletonTransformsOverride; } //var flverMaterialList = new List<FLVER2.Material>(); foreach (var material in scene.Materials) { string[] materialNameSplit = material.Name.Split('|'); string mtd = materialNameSplit.Length > 1 ? materialNameSplit[1].Trim() + ".mtd" : null; // If MTD doesn't exist, use original mtd = MaterialInfoBankPerGame[settings.Game].FallbackToDefaultMtdIfNecessary(mtd, Logger); //ErrorTODO: materialNameSplit should be 2 items long. var flverMaterial = new FLVER2.Material(materialNameSplit[0].Trim(), mtd, 0); void AddTextureSlot(TextureSlot slot, string ingameSlot) { flverMaterial.Textures.Add(new FLVER2.Texture(type: ingameSlot, path: slot.FilePath != null ? Path.GetFullPath(slot.FilePath) : "", scale: System.Numerics.Vector2.One, 1, true, 0, 0, 0)); string texName = Path.GetFileNameWithoutExtension(slot.FilePath); byte[] texData = scene.GetEmbeddedTexture(slot.FilePath)?.CompressedData; if (texData != null) { var ddsFormat = TPFTextureFormatFinder.GetTpfFormatFromDdsBytes(texData); result.Textures.Add(new TPF.Texture(texName, format: ddsFormat, flags1: 0, bytes: texData)); } } var materialDefinition = MaterialInfoBankPerGame[settings.Game].MaterialDefs[mtd.ToLower()]; var texChanDefs = materialDefinition.TextureChannels; foreach (var kvp in texChanDefs) { if (kvp.Key.Index == 0) { if (kvp.Key.Semantic == TextureChannelSemantic.Diffuse) { AddTextureSlot(material.TextureDiffuse, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Specular) { AddTextureSlot(material.TextureSpecular, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Normals) { AddTextureSlot(material.TextureNormal, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Emissive) { AddTextureSlot(material.TextureEmissive, kvp.Value); } else { flverMaterial.Textures.Add(new FLVER2.Texture(type: kvp.Value, path: string.Empty, scale: System.Numerics.Vector2.One, 0, false, 0, 0, 0)); } } } if (materialDefinition.GXItems.Count > 0) { flverMaterial.GXIndex = flver.GXLists.Count; var gxList = new FLVER2.GXList(); for (int i = 0; i < materialDefinition.GXItems.Count; i++) { var gxid = materialDefinition.GXItems[i].GXID; var unk04 = materialDefinition.GXItems[i].Unk04; byte[] data = MaterialInfoBankPerGame[settings.Game].DefaultGXItemDataExamples[mtd][i]; gxList.Add(new FLVER2.GXItem(gxid, unk04, data)); } flver.GXLists.Add(gxList); } flver.Materials.Add(flverMaterial); //flverMaterialList.Add(flverMaterial); } //var properBoneParentRegistry = new Dictionary<Bone, string>(); //foreach (var mesh in scene.Meshes) //{ // foreach (var b in mesh.Bones) // { // bool alreadyRegistered = false; // foreach (var bone in properBoneParentRegistry.Keys) // { // if (bone.Name == b.Name) // { // alreadyRegistered = true; // break; // } // } // if (alreadyRegistered) // continue; // mesh. // properBoneParentRegistry.Add(b, b.) // } //} if (settings.BoneNameRemapper != null) { foreach (var bn in settings.BoneNameRemapper) { var bone = flver.Bones.FindIndex(b => b.Name == bn.Key); if (bone >= 0) { flver.Bones[bone].Name = bn.Value; } } } foreach (var mesh in scene.Meshes) { var flverMesh = new FLVER2.Mesh(); flverMesh.BoundingBox = new FLVER2.Mesh.BoundingBoxes(); //TODO: ACTUALLY READ FROM THINGS flverMesh.Dynamic = 1; // Register mesh transform bone: //flverMesh.DefaultBoneIndex = flver.Bones.Count; //int flverLastRootBoneIndex = flver.Bones.FindLastIndex(b => b.ParentIndex == -1); //// Register this new bone as a sibling. //if (flverLastRootBoneIndex >= 0) // flver.Bones[flverLastRootBoneIndex].NextSiblingIndex = (short)flverMesh.DefaultBoneIndex; //flver.Bones.Add(new FLVER.Bone() //{ // Name = mesh.Name, // Translation = NVector3.Zero, // Rotation = NVector3.Zero, // Scale = NVector3.One, // BoundingBoxMin = NVector3.One * -0.05f, // BoundingBoxMax = NVector3.One * 0.05f, // // Cross-register sibling from above. // PreviousSiblingIndex = (short)flverLastRootBoneIndex, // NextSiblingIndex = -1, // ParentIndex = -1, // ChildIndex = -1, // Unk3C = 1, //}); int meshUVCount = 0; for (int i = 0; i < mesh.UVComponentCount.Length; i++) { if (mesh.UVComponentCount[i] > 0) { meshUVCount++; } } if (mesh.PrimitiveType != PrimitiveType.Triangle) { Console.WriteLine(); } var flverFaceSet = new FLVER2.FaceSet(); //flverFaceSet.TriangleStrip = true; // Handle vertex buffers / layouts: flverMesh.MaterialIndex = mesh.MaterialIndex; //var newMat = flverMaterialList[mesh.MaterialIndex]; //var indexOfNewMat = flver.Materials.IndexOf(newMat); //if (indexOfNewMat >= 0) //{ // flverMesh.MaterialIndex = indexOfNewMat; //} //else //{ // flverMesh.MaterialIndex = flver.Materials.Count; // flver.Materials.Add(newMat); //} var flverMaterial = flver.Materials[flverMesh.MaterialIndex]; var matDefinition = MaterialInfoBankPerGame[settings.Game].MaterialDefs[flverMaterial.MTD.ToLower()]; var defaultBufferDeclaration = matDefinition.AcceptableVertexBufferDeclarations[0]; Dictionary <FLVER.LayoutSemantic, int> requiredVertexBufferMembers = new Dictionary <FLVER.LayoutSemantic, int>(); foreach (var buff in defaultBufferDeclaration.Buffers) { foreach (var m in buff) { if (!requiredVertexBufferMembers.ContainsKey(m.Semantic)) { requiredVertexBufferMembers.Add(m.Semantic, 0); } requiredVertexBufferMembers[m.Semantic]++; } int nextLayoutIndex = flver.BufferLayouts.Count; flver.BufferLayouts.Add(buff); var vertBuffer = new FLVER2.VertexBuffer(nextLayoutIndex); flverMesh.VertexBuffers.Add(vertBuffer); } flverMesh.Vertices = new List <FLVER.Vertex>(mesh.VertexCount); for (int i = 0; i < mesh.VertexCount; i++) { var newVert = new FLVER.Vertex(uvCapacity: meshUVCount, //TODO: Figure out what multiple tangents are used for etc and implement all // of that into the XML vert layout system stuff etc etc. tangentCapacity: mesh.HasTangentBasis ? 1 : 0, colorCapacity: mesh.VertexColorChannelCount); newVert.Position = NVector3.Transform(mesh.Vertices[i].ToNumerics(), flverSceneMatrix); flver.Header.UpdateBoundingBox(newVert.Position); if (flverMesh.BoundingBox != null) { flverMesh.UpdateBoundingBox(newVert.Position); } newVert.Normal = NVector3.Normalize(NVector3.TransformNormal(mesh.Normals[i].ToNumerics(), flverSceneMatrix)); //TODO: TEST THIS AGAINST OTHER GAMES ETC //newVert.NormalW = 127; if (mesh.HasTangentBasis) { //ErrorTODO: Throw error if mesh somehow has tangents but not normals. var tan = mesh.Tangents[i]; var bitanXYZ = mesh.BiTangents[i]; //TODO: Check Bitangent W calculation var bitanW = Vector3D.Dot(Vector3D.Cross(tan, mesh.Normals[i]), bitanXYZ) >= 0 ? 1 : -1; var bitanXYZTransformed = NVector3.Normalize(NVector3.TransformNormal(bitanXYZ.ToNumerics(), flverSceneMatrix)); newVert.Tangents.Add(new System.Numerics.Vector4(bitanXYZTransformed, bitanW)); //TODO: CHECK THIS AND SEE WTF IT EVEN IS SUPPOSED TO BE newVert.Bitangent = new System.Numerics.Vector4( NVector3.TransformNormal(tan.ToNumerics(), flverSceneMatrix), 0); } for (int j = 0; j < meshUVCount; j++) { var uv = mesh.TextureCoordinateChannels[j][i]; newVert.UVs.Add(new NVector3(uv.X, 1 - uv.Y, uv.Z)); } for (int j = 0; j < mesh.VertexColorChannelCount; j++) { newVert.Colors.Add(mesh.VertexColorChannels[j][i].ToFlverVertexColor()); } for (int j = 0; j < 4; j++) { newVert.BoneIndices[j] = -1; } newVert.EnsureLayoutMembers(requiredVertexBufferMembers); flverMesh.Vertices.Add(newVert); } if (usesIndirectBones) { var bonesInMesh = mesh.Bones.OrderByDescending(mb => mb.VertexWeightCount).ToList(); foreach (var bone in bonesInMesh) { var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name); if (!flverMesh.BoneIndices.Contains(boneIndex)) { flverMesh.BoneIndices.Add(boneIndex); } } flverMesh.BoneIndices = flverMesh.BoneIndices.OrderBy(idx => idx).ToList(); } foreach (var bone in mesh.Bones) { var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name); if (boneIndex == -1) { Logger.LogWarning($"No bone with exact name '{bone.Name}' found. Looking for a bone that starts with that name"); boneIndex = flver.Bones.FindIndex(b => b.Name.StartsWith(bone.Name)); } var boneDoesNotExist = false; // Mark bone as not-dummied-out since there is geometry skinned to it. if (boneIndex >= 0 && boneIndex < flver.Bones.Count) { flver.Bones[boneIndex].Unk3C = 0; } else { Logger.LogWarning($"Vertex skinned to bone '{bone.Name}' which does NOT exist in the skeleton."); boneDoesNotExist = true; } int GetNextAvailableBoneSlotOfVert(int vertIndex) { if (flverMesh.Vertices[vertIndex].BoneIndices[0] < 0) { return(0); } else if (flverMesh.Vertices[vertIndex].BoneIndices[1] < 0) { return(1); } else if (flverMesh.Vertices[vertIndex].BoneIndices[2] < 0) { return(2); } else if (flverMesh.Vertices[vertIndex].BoneIndices[3] < 0) { return(3); } else { return(-1); } } foreach (var weight in bone.VertexWeights) { int boneSlot = GetNextAvailableBoneSlotOfVert(weight.VertexID); if (boneSlot >= 0) { var indexToAssign = usesIndirectBones ? flverMesh.BoneIndices.IndexOf(boneIndex) : boneIndex; if (indexToAssign == -1) { Console.WriteLine("fatcat"); } flverMesh.Vertices[weight.VertexID].BoneIndices[boneSlot] = boneDoesNotExist ? 0 : indexToAssign; flverMesh.Vertices[weight.VertexID].BoneWeights[boneSlot] = boneDoesNotExist ? 0 : weight.Weight; if (!boneDoesNotExist) { flver.Bones[boneIndex].UpdateBoundingBox(flver.Bones, flverMesh.Vertices[weight.VertexID].Position); } } } } for (int i = 0; i < flverMesh.Vertices.Count; i++) { float weightMult = 1 / ( flverMesh.Vertices[i].BoneWeights[0] + flverMesh.Vertices[i].BoneWeights[1] + flverMesh.Vertices[i].BoneWeights[2] + flverMesh.Vertices[i].BoneWeights[3]); for (int j = 0; j < 4; j++) { //flverMesh.Vertices[i].BoneWeights[j] = flverMesh.Vertices[i].BoneWeights[j] * weightMult; if (flverMesh.Vertices[i].BoneIndices[j] < 0) { flverMesh.Vertices[i].BoneIndices[j] = 0; } } //TODO: TEST THIS AGAINST OTHER GAMES ETC if (!requiredVertexBufferMembers.ContainsKey(FLVER.LayoutSemantic.BoneIndices)) { flverMesh.Vertices[i].NormalW = flverMesh.Vertices[i].BoneIndices[0]; } } //foreach (var face in mesh.Faces) //{ // //TODO: See if resets need to be added inbetween or anything. // flverFaceSet.Indices.AddRange(face.Indices); //} flverFaceSet.Indices.AddRange(mesh.GetIndices()); flverMesh.FaceSets.Add(flverFaceSet); GenerateLodAndMotionBlurFacesets(flverMesh); flver.Meshes.Add(flverMesh); } // DEBUGGING //flver.Bones.RemoveAt(0); //foreach (var mm in flver.Meshes) // for (int mbi = 0; mbi < mm.BoneIndices.Count; mbi++) // mm.BoneIndices[mbi] = mm.BoneIndices[mbi] - 1; //foreach (var b in flver.Bones) //{ // if (b.ParentIndex >= 0) // b.ParentIndex--; // if (b.ChildIndex >= 0) // b.ChildIndex--; // if (b.NextSiblingIndex >= 0) // b.NextSiblingIndex--; // if (b.PreviousSiblingIndex >= 0) // b.PreviousSiblingIndex--; //} /////////////////// foreach (var b in flver.Bones) { if (settings.SkeletonTransformsOverride != null) { var match = settings.SkeletonTransformsOverride.FindIndex(bn => bn.Name == b.Name); if (match >= 0) { b.Translation = settings.SkeletonTransformsOverride[match].Translation; b.Rotation = settings.SkeletonTransformsOverride[match].Rotation; b.Scale = settings.SkeletonTransformsOverride[match].Scale; } } if (float.IsInfinity(b.BoundingBoxMin.X) || float.IsInfinity(b.BoundingBoxMin.Y) || float.IsInfinity(b.BoundingBoxMin.Z) || float.IsInfinity(b.BoundingBoxMax.X) || float.IsInfinity(b.BoundingBoxMax.Y) || float.IsInfinity(b.BoundingBoxMax.Z)) { b.BoundingBoxMin = NVector3.One * -0.1f; b.BoundingBoxMax = NVector3.One * 0.1f; } } return(result); }
/** * Constructor for a ray * * @param direction direction ray * @param point beginning of the ray */ public Line(Vector3d direction, Point3d point) { this.direction = Vector3d.Normalize(direction); this.point = point; //direction.normalize(); }
/// <summary> /// Finds a supporting entity, the contact location, and the contact normal. /// </summary> /// <param name="location">Contact point between the wheel and the support.</param> /// <param name="normal">Contact normal between the wheel and the support.</param> /// <param name="suspensionLength">Length of the suspension at the contact.</param> /// <param name="supportingCollidable">Collidable supporting the wheel, if any.</param> /// <param name="entity">Supporting object.</param> /// <param name="material">Material of the wheel.</param> /// <returns>Whether or not any support was found.</returns> protected internal override bool FindSupport(out System.Numerics.Vector3 location, out System.Numerics.Vector3 normal, out float suspensionLength, out Collidable supportingCollidable, out Entity entity, out Material material) { suspensionLength = float.MaxValue; location = Toolbox.NoVector; supportingCollidable = null; entity = null; normal = Toolbox.NoVector; material = null; Collidable testCollidable; RayHit rayHit; bool hit = false; for (int i = 0; i < detector.CollisionInformation.pairs.Count; i++) { var pair = detector.CollisionInformation.pairs[i]; testCollidable = (pair.BroadPhaseOverlap.entryA == detector.CollisionInformation ? pair.BroadPhaseOverlap.entryB : pair.BroadPhaseOverlap.entryA) as Collidable; if (testCollidable != null) { if (CollisionRules.CollisionRuleCalculator(this, testCollidable) == CollisionRule.Normal && testCollidable.RayCast(new Ray(wheel.suspension.worldAttachmentPoint, wheel.suspension.worldDirection), wheel.suspension.restLength, out rayHit) && rayHit.T < suspensionLength) { suspensionLength = rayHit.T; EntityCollidable entityCollidable; if ((entityCollidable = testCollidable as EntityCollidable) != null) { entity = entityCollidable.Entity; material = entityCollidable.Entity.Material; } else { entity = null; supportingCollidable = testCollidable; var materialOwner = testCollidable as IMaterialOwner; if (materialOwner != null) { material = materialOwner.Material; } } location = rayHit.Location; normal = rayHit.Normal; hit = true; } } } if (hit) { if (suspensionLength > 0) { normal.Normalize(); } else { Vector3Ex.Negate(ref wheel.suspension.worldDirection, out normal); } return(true); } return(false); }