third person character update

This commit is contained in:
Turanszki Janos
2020-04-19 20:21:29 +01:00
parent f9e023d897
commit 4cbe366ac9
9 changed files with 329 additions and 51 deletions
@@ -719,6 +719,8 @@ Sphere defined by center Vector and radius. Can be intersected with other primit
- Intersects(Ray ray) : bool result
- GetCenter() : Vector result
- GetRadius() : float result
- SetCenter(Vector value)
- SetRadius(float value)
### Input
Query input devices
+36
View File
@@ -249,6 +249,8 @@ namespace wiIntersect_BindLua
lunamethod(Sphere_BindLua, Intersects),
lunamethod(Sphere_BindLua, GetCenter),
lunamethod(Sphere_BindLua, GetRadius),
lunamethod(Sphere_BindLua, SetCenter),
lunamethod(Sphere_BindLua, SetRadius),
{ NULL, NULL }
};
Luna<Sphere_BindLua>::PropertyType Sphere_BindLua::properties[] = {
@@ -324,5 +326,39 @@ namespace wiIntersect_BindLua
wiLua::SSetFloat(L, sphere.radius);
return 1;
}
int Sphere_BindLua::SetCenter(lua_State* L)
{
int argc = wiLua::SGetArgCount(L);
if (argc > 0)
{
Vector_BindLua* cV = Luna<Vector_BindLua>::lightcheck(L, 1);
if (cV)
{
XMStoreFloat3(&sphere.center, cV->vector);
}
else
{
wiLua::SError(L, "SetCenter(Vector value) requires first argument to be of Vector type!");
}
}
else
{
wiLua::SError(L, "SetCenter(Vector value) not enough arguments!");
}
return 0;
}
int Sphere_BindLua::SetRadius(lua_State* L)
{
int argc = wiLua::SGetArgCount(L);
if (argc > 0)
{
sphere.radius = wiLua::SGetFloat(L, 1);
}
else
{
wiLua::SError(L, "SetRadius(float value) not enough arguments!");
}
return 0;
}
}
+2
View File
@@ -62,5 +62,7 @@ namespace wiIntersect_BindLua
int Intersects(lua_State* L);
int GetCenter(lua_State* L);
int GetRadius(lua_State* L);
int SetCenter(lua_State* L);
int SetRadius(lua_State* L);
};
}
+178
View File
@@ -2729,4 +2729,182 @@ namespace wiScene
return result;
}
#include <DirectXCollision.h>
SceneIntersectSphereResult SceneIntersectSphere(const SPHERE& sphere, uint32_t renderTypeMask, uint32_t layerMask, const Scene& scene)
{
SceneIntersectSphereResult result;
XMVECTOR vCenter = XMLoadFloat3(&sphere.center);
XMVECTOR vRadius = XMVectorReplicatePtr(&sphere.radius);
XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius);
if (scene.objects.GetCount() > 0)
{
for (size_t i = 0; i < scene.aabb_objects.GetCount(); ++i)
{
const AABB& aabb = scene.aabb_objects[i];
if (!sphere.intersects(aabb))
{
continue;
}
const ObjectComponent& object = scene.objects[i];
if (object.meshID == INVALID_ENTITY)
{
continue;
}
if (!(renderTypeMask & object.GetRenderTypes()))
{
continue;
}
Entity entity = scene.aabb_objects.GetEntity(i);
const LayerComponent* layer = scene.layers.GetComponent(entity);
if (layer != nullptr && !(layer->GetLayerMask() & layerMask))
{
continue;
}
const MeshComponent& mesh = *scene.meshes.GetComponent(object.meshID);
const SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object.meshID);
const bool softbody_active = softbody != nullptr && !softbody->vertex_positions_simulation.empty();
const XMMATRIX objectMat = object.transform_index >= 0 ? XMLoadFloat4x4(&scene.transforms[object.transform_index].world) : XMMatrixIdentity();
const ArmatureComponent* armature = mesh.IsSkinned() ? scene.armatures.GetComponent(mesh.armatureID) : nullptr;
int subsetCounter = 0;
for (auto& subset : mesh.subsets)
{
for (size_t i = 0; i < subset.indexCount; i += 3)
{
const uint32_t i0 = mesh.indices[subset.indexOffset + i + 0];
const uint32_t i1 = mesh.indices[subset.indexOffset + i + 1];
const uint32_t i2 = mesh.indices[subset.indexOffset + i + 2];
XMVECTOR p0;
XMVECTOR p1;
XMVECTOR p2;
if (softbody_active)
{
p0 = softbody->vertex_positions_simulation[i0].LoadPOS();
p1 = softbody->vertex_positions_simulation[i1].LoadPOS();
p2 = softbody->vertex_positions_simulation[i2].LoadPOS();
}
else
{
if (armature == nullptr)
{
p0 = XMLoadFloat3(&mesh.vertex_positions[i0]);
p1 = XMLoadFloat3(&mesh.vertex_positions[i1]);
p2 = XMLoadFloat3(&mesh.vertex_positions[i2]);
}
else
{
p0 = SkinVertex(mesh, *armature, i0);
p1 = SkinVertex(mesh, *armature, i1);
p2 = SkinVertex(mesh, *armature, i2);
}
}
p0 = XMVector3Transform(p0, objectMat);
p1 = XMVector3Transform(p1, objectMat);
p2 = XMVector3Transform(p2, objectMat);
// Compute the plane of the triangle (has to be normalized).
XMVECTOR N = XMVector3Normalize(XMVector3Cross(XMVectorSubtract(p1, p0), XMVectorSubtract(p2, p0)));
// Assert that the triangle is not degenerate.
assert(!XMVector3Equal(N, XMVectorZero()));
// Find the nearest feature on the triangle to the sphere.
XMVECTOR Dist = XMVector3Dot(XMVectorSubtract(vCenter, p0), N);
if (XMVectorGetX(Dist) > 0)
{
continue;
}
// If the center of the sphere is farther from the plane of the triangle than
// the radius of the sphere, then there cannot be an intersection.
XMVECTOR NoIntersection = XMVectorLess(Dist, XMVectorNegate(vRadius));
NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Dist, vRadius));
// Project the center of the sphere onto the plane of the triangle.
XMVECTOR Point = XMVectorNegativeMultiplySubtract(N, Dist, vCenter);
XMVECTOR planeIntersection = Point;
// Is it inside all the edges? If so we intersect because the distance
// to the plane is less than the radius.
XMVECTOR Intersection = DirectX::Internal::PointOnPlaneInsideTriangle(Point, p0, p1, p2);
bool inside = XMVector4EqualInt(XMVectorAndCInt(Intersection, NoIntersection), XMVectorTrueInt());
XMVECTOR bestPoint = Point;
float bestDist = inside ? 0 : FLT_MAX;
// Find the nearest point on each edge.
// Edge 0,1
Point = DirectX::Internal::PointOnLineSegmentNearestPoint(p0, p1, vCenter);
// If the distance to the center of the sphere to the point is less than
// the radius of the sphere then it must intersect.
Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
float d = abs(XMVectorGetX(XMVector3LinePointDistance(p0, p1, planeIntersection)));
if (d < bestDist)
{
bestDist = d;
bestPoint = Point;
}
// Edge 1,2
Point = DirectX::Internal::PointOnLineSegmentNearestPoint(p1, p2, vCenter);
// If the distance to the center of the sphere to the point is less than
// the radius of the sphere then it must intersect.
Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
d = abs(XMVectorGetX(XMVector3LinePointDistance(p1, p2, planeIntersection)));
if (d < bestDist)
{
bestDist = d;
bestPoint = Point;
}
// Edge 2,0
Point = DirectX::Internal::PointOnLineSegmentNearestPoint(p2, p0, vCenter);
// If the distance to the center of the sphere to the point is less than
// the radius of the sphere then it must intersect.
Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
d = abs(XMVectorGetX(XMVector3LinePointDistance(p2, p0, planeIntersection)));
if (d < bestDist)
{
bestDist = d;
bestPoint = Point;
}
bool intersects = XMVector4EqualInt(XMVectorAndCInt(Intersection, NoIntersection), XMVectorTrueInt());
if (intersects && bestDist < result.distance_to_polygon)
{
result.entity = entity;
result.depth = sphere.radius - XMVectorGetX(XMVector3Length(vCenter - bestPoint));
XMStoreFloat3(&result.position, bestPoint);
XMStoreFloat3(&result.normal, XMVector3Normalize(vCenter - bestPoint));
}
}
subsetCounter++;
}
}
}
return result;
}
}
+11
View File
@@ -1353,5 +1353,16 @@ namespace wiScene
// layerMask : filter based on layer
// scene : the scene that will be traced against the ray
PickResult Pick(const RAY& ray, uint32_t renderTypeMask = RENDERTYPE_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene());
struct SceneIntersectSphereResult
{
wiECS::Entity entity = wiECS::INVALID_ENTITY;
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
XMFLOAT3 normal = XMFLOAT3(0, 0, 0);
float depth = 0;
float distance_to_polygon = FLT_MAX;
};
SceneIntersectSphereResult SceneIntersectSphere(const SPHERE& sphere, uint32_t renderTypeMask = RENDERTYPE_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene());
}
+51
View File
@@ -139,6 +139,56 @@ int Pick(lua_State* L)
return 0;
}
int SceneIntersectSphere(lua_State* L)
{
int argc = wiLua::SGetArgCount(L);
if (argc > 0)
{
Sphere_BindLua* sphere = Luna<Sphere_BindLua>::lightcheck(L, 1);
if (sphere != nullptr)
{
uint32_t renderTypeMask = RENDERTYPE_OPAQUE;
uint32_t layerMask = 0xFFFFFFFF;
Scene* scene = &wiScene::GetScene();
if (argc > 1)
{
renderTypeMask = (uint32_t)wiLua::SGetInt(L, 2);
if (argc > 2)
{
int mask = wiLua::SGetInt(L, 3);
layerMask = *reinterpret_cast<uint32_t*>(&mask);
if (argc > 3)
{
Scene_BindLua* custom_scene = Luna<Scene_BindLua>::lightcheck(L, 4);
if (custom_scene)
{
scene = custom_scene->scene;
}
else
{
wiLua::SError(L, "SceneIntersectSphere(Sphere sphere, opt PICKTYPE pickType, opt uint layerMask, opt Scene scene) last argument is not of type Scene!");
}
}
}
}
auto& pick = wiScene::SceneIntersectSphere(sphere->sphere, renderTypeMask, layerMask, *scene);
wiLua::SSetInt(L, (int)pick.entity);
Luna<Vector_BindLua>::push(L, new Vector_BindLua(XMLoadFloat3(&pick.position)));
Luna<Vector_BindLua>::push(L, new Vector_BindLua(XMLoadFloat3(&pick.normal)));
wiLua::SSetFloat(L, pick.depth);
return 4;
}
wiLua::SError(L, "SceneIntersectSphere(Sphere sphere, opt PICKTYPE pickType, opt uint layerMask, opt Scene scene) first argument must be of Ray type!");
}
else
{
wiLua::SError(L, "SceneIntersectSphere(Sphere sphere, opt PICKTYPE pickType, opt uint layerMask, opt Scene scene) not enough arguments!");
}
return 0;
}
void Bind()
{
@@ -168,6 +218,7 @@ void Bind()
wiLua::GetGlobal()->RegisterFunc("GetScene", GetScene);
wiLua::GetGlobal()->RegisterFunc("LoadModel", LoadModel);
wiLua::GetGlobal()->RegisterFunc("Pick", Pick);
wiLua::GetGlobal()->RegisterFunc("SceneIntersectSphere", SceneIntersectSphere);
Luna<Scene_BindLua>::Register(L);
Luna<NameComponent_BindLua>::Register(L);
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wiVersion
// minor features, major updates
const int minor = 39;
// minor bug fixes, alterations, refactors, updates
const int revision = 71;
const int revision = 72;
long GetVersion()
Binary file not shown.
+48 -50
View File
@@ -32,9 +32,6 @@ Character = {
face = Vector(0,0,1), -- forward direction
force = Vector(),
velocity = Vector(),
ray = Ray(Vector(),Vector()),
o = INVALID_ENTITY, -- collision prop with scene (entity)
p,n = Vector(), -- collision props with scene (position,normal)
savedPointerPos = Vector(),
moveSpeed = 90,
layerMask = 0x2, -- The character will be tagged to use this layer, so scene Picking can filter out the character
@@ -87,9 +84,12 @@ Character = {
model_transform.ClearTransform()
dir = vector.Transform(dir:Normalize(), target_transform.GetMatrix())
dir.SetY(0)
dir = dir.Normalize()
local dot = vector.Dot(self.face, dir)
if(dot < 0) then
self.face = vector.TransformNormal(self.face, matrix.RotationY(3.1415 * 0.01))
end
self.face = vector.Lerp(self.face, dir, 0.2);
self.face = self.face.Normalize()
self.face.Normalize()
model_transform.MatrixTransform(matrix.LookTo(Vector(),self.face):Inverse())
model_transform.Scale(self.scale)
model_transform.Rotate(self.rotation)
@@ -97,7 +97,9 @@ Character = {
model_transform.UpdateTransform()
scene.Component_Detach(self.target)
scene.Component_Attach(self.target, self.model)
self.force = vector.Add(self.force, self.face:Multiply(Vector(f,f,f)))
if(dot > 0) then
self.force = vector.Add(self.force, self.face:Multiply(Vector(f,f,f)))
end
self.state = self.states.WALK
end,
@@ -163,13 +165,6 @@ Character = {
local model_transform = scene.Component_GetTransform(self.model)
local target_transform = scene.Component_GetTransform(self.target)
-- gravity:
self.force = vector.Add(self.force, Vector(0,-9.8 * 20,0))
-- apply force:
self.velocity = vector.Add(self.velocity, vector.Multiply(self.force, 0.016))
self.force = Vector(0,0,0,0)
-- state and animation update
if(self.state == self.states.STAND) then
scene.Component_GetAnimation(self.walk_anim).Stop()
@@ -198,50 +193,53 @@ Character = {
end
self.state_prev = self.state
-- front block shoots multiple rays in front to try to find obstruction
local rotations = {0, 3.1415*0.3, -3.1415*0.3}
for i,rot in ipairs(rotations) do
local origin = vector.Add(model_transform.GetPosition(), Vector(0,1,0)) -- this ray starts a little above character ground position
local dir = vector.Transform(self.face, matrix.RotationY(rot))
local ray2 = Ray(origin,dir)
local o2,p2,n2 = Pick(ray2, PICK_OPAQUE, ~self.layerMask)
local dist = vector.Subtract(origin,p2):Length()
if(o2 ~= INVALID_ENTITY and dist < 1) then
-- run along wall instead of going through it
-- gravity:
self.force = vector.Add(self.force, Vector(0,-9.8 * 10,0))
-- apply force:
self.velocity = vector.Add(self.velocity, vector.Multiply(self.force, 0.016))
self.force = Vector(0,0,0,0)
-- Sphere collider for character:
local sphere = Sphere(vector.Add(model_transform.GetPosition(), Vector(0,1.1)), 1)
local pp = sphere.GetCenter()
local intersection = false
local ccd = 0
local ccd_max = 5
while(ccd < ccd_max) do
ccd = ccd + 1
local step = vector.Multiply(self.velocity, 1.0 / ccd_max * 0.016)
local prevpos = sphere.GetCenter()
sphere.SetCenter(vector.Add(prevpos, step))
local o2, p2, n2, depth = SceneIntersectSphere(sphere, PICK_OPAQUE, ~self.layerMask)
if(o2 ~= INVALID_ENTITY) then
DrawPoint(p2,0.5,Vector(1,1,0,1))
DrawLine(p2, vector.Add(p2, n2), Vector(1,1,0,1))
-- Slide on surface:
local velocityLen = self.velocity.Length()
local velocityNormalized = self.velocity.Normalize()
local undesiredMotion = n2:Multiply(vector.Dot(velocityNormalized, n2))
local desiredMotion = vector.Subtract(velocityNormalized, undesiredMotion)
self.velocity = vector.Multiply(desiredMotion, velocityLen)
end
end
-- check what is below the character
self.ray = Ray(target_transform.GetPosition(),Vector(0,-1,0))
local pPrev = self.p
self.o,self.p,self.n = Pick(self.ray, PICK_OPAQUE, ~self.layerMask)
if(self.o == INVALID_ENTITY) then
self.p=pPrev -- if nothing, go back to previous position
end
-- apply velocity:
model_transform.Translate(vector.Multiply(self.velocity, 0.016))
model_transform.UpdateTransform()
-- check if we are below or on the ground:
local posY = model_transform.GetPosition().GetY()
if(posY <= self.p.GetY() and self.velocity.GetY()<=0) then
self.state = self.states.STAND
if(self.o == INVALID_ENTITY) then
model_transform.Translate(vector.Subtract(self.p,model_transform.GetPosition())) -- snap back to last succesfully traced position
else
model_transform.Translate(Vector(0,self.p.GetY()-posY,0)) -- snap to ground
intersection = true
sphere.SetCenter(prevpos)
end
self.velocity.SetY(0) -- don't fall below ground
self.velocity = vector.Multiply(self.velocity, 0.8) -- slow down on ground
else
self.velocity = vector.Multiply(self.velocity, 0.94) -- slow down in air
end
--DrawPoint(sphere.GetCenter(), 0.5, Vector(0,1,0,1))
if intersection then
self.velocity = vector.Multiply(self.velocity, 0.8) -- ground friction
else
self.velocity = vector.Multiply(self.velocity, 0.94) -- air friction
end
if(vector.Length(self.velocity) < 0.001) then
self.state = self.states.STAND
end
model_transform.Translate(vector.Subtract(sphere.GetCenter(), pp))
model_transform.UpdateTransform()
-- try to put water ripple if character head is directly above water
local head_transform = scene.Component_GetTransform(self.head)
@@ -441,7 +439,7 @@ runProcess(function()
--face
DrawLine(target_transform.GetPosition(),target_transform.GetPosition():Add(player.face:Normalize()),Vector(0,1,0,1))
--intersection
DrawAxis(player.p,0.5)
--DrawAxis(player.p,0.5)
-- camera target box and axis
DrawBox(target_transform.GetMatrix())