Move and rotate object around pivot and resume from where it stopped - math

I'd like to move an object around another - just as if the one object was a child of the other. This is GDscript - Godot Engine 3.2, but the logic should be very similar to other game engines.
Whenever I hold spacebar the green cube follows the blue cubes rotation.
First GIF demonstrates the green cube starting at position Vector3(0, 4, 0) without any rotation applied. This works perfectly fine.
In the second GIF I'm holding and releasing spacebar repeatedly. I would expect the green cube to continue from where it left, but instead it "jumps" to a new position and continues from there.
Code below does not include the actual rotation of the blue cube (pivot point), but only the calculations needed to move/rotate the green cube. Rotation of the blue cube is not an issue. Also, rotation is just for the sake of demonstration - in real scenario the blue cube would be moving around as well.
Rotations are calculated using quaternion, but this is not a requirement.
extends Node
var _parent
var _child
var _subject
var _positionOffset: Vector3
var _rotationOffset: Quat
func _ready():
_parent = get_parent()
_child = $"/root/Main/Child"
func _input(event):
if event is InputEventKey:
if event.scancode == KEY_SPACE:
if event.is_action_pressed("ui_accept") and _child != null:
_subject = _child
_set_subject_offset(_parent.transform, _child.transform)
elif event.is_action_released("ui_accept"):
_subject = null
func _set_subject_offset(pivot: Transform, subject: Transform):
_positionOffset = (pivot.origin - subject.origin)
_rotationOffset = pivot.basis.get_rotation_quat().inverse() * subject.basis.get_rotation_quat()
func _rotate_around_pivot(subject_position: Vector3, pivot: Vector3, subject_rotation: Quat):
return pivot + (subject_rotation * (subject_position - pivot))
func _physics_process(_delta):
if _subject == null: return
var target_position = _parent.transform.origin - _positionOffset
var target_rotation = _parent.transform.basis.get_rotation_quat() * _rotationOffset
_subject.transform.origin = _rotate_around_pivot(target_position, _parent.transform.origin, target_rotation)
_subject.set_rotation(target_rotation.get_euler())
I feel like I'm missing something obvious.

You can easily achieve this by temporarily parenting the subject to the pivot point while preserving the global transform:
extends Spatial
onready var obj1: Spatial = $Object1
onready var obj2: Spatial = $Object2
func reparent(obj: Spatial, new_parent: Spatial):
# Preserve the global transform while reparenting
var old_trans := obj.global_transform
obj.get_parent().remove_child(obj)
new_parent.add_child(obj)
obj.global_transform = old_trans
func _physics_process(delta: float):
obj1.rotate_z(delta)
func _input(event: InputEvent):
if event.is_action_pressed("ui_accept"):
reparent(obj2, obj1)
elif event.is_action_released("ui_accept"):
reparent(obj2, self)
If reparenting isn't feasible, you could instead parent a RemoteTransform to the pivot, and have that push its transform to the object you want to rotate:
extends Spatial
onready var remote_trans: RemoteTransform = $RemoteTransform
func _process(delta):
rotate_z(delta)
func attach(n: Node):
# move the remote so the target maintains its transform
remote_trans.global_transform = n.global_transform
remote_trans.remote_path = n.get_path()
func detach():
remote_trans.remote_path = ""

repost, Thank You path9263, answered Jun 4, 2019 by path9263 (134 points)
func rotate_around(obj:Spatial, point:Vector3, axis:Vector3, phi:float):
# https://godotengine.org/qa/45609/how-do-you-rotate-spatial-node-around-axis-given-point-space?show=45970#a45970
obj.global_translate(-point)
obj.transform = obj.transform.rotated(axis, phi)
obj.global_translate(point)
the below functions do not work
func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
# #4: busted, no rotation
# https://stackoverflow.com/a/59939086/1695680
# Preserve the global transform while reparenting
var pivot_spatial = Spatial.new()
pivot_spatial.translation = pivot_point
var parent_orig = transform_me.get_parent()
var transform_orig = transform_me.global_transform
parent_orig.remove_child(transform_me)
pivot_spatial.add_child(transform_me)
transform_me.global_transform = transform_orig
pivot_spatial.rotate(axis, phi)
var transform_rotated = transform_me.global_transform
pivot_spatial.remove_child(transform_me)
parent_orig.add_child(transform_me)
transform_me.global_transform = transform_orig
func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
# busted, no rotate
# https://stackoverflow.com/a/59939086/1695680
var remote_trans : RemoteTransform = RemoteTransform.new()
remote_trans.global_transform = transform_me.global_transform
remote_trans.remote_path = transform_me.get_path()
remote_trans.rotate(axis, phi)
remote_trans.remote_path = ""
func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
# busted, local rotate
# https://godotforums.org/discussion/comment/43335/#Comment_43335
var gto_orig = transform_me.global_transform.origin
transform_me.global_transform.origin = pivot_point
transform_me.rotate(axis, phi)
transform_me.global_transform.origin = gto_orig
func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
# busted, no rotate
# https://godotengine.org/qa/34248/rotate-around-a-fixed-point-in-3d-space?show=38928#a38928
var start_position : Vector3 = transform_me.translation
var pivot_radius : Vector3 = start_position - pivot_point
var pivot_transform : Transform = Transform(transform.basis, pivot_point)
var transform = pivot_transform.rotated(axis, phi).translated(pivot_radius)
transform.xform(transform_me)

Related

How to implement player state machine

I am very new to godot, and making 2d action platformer. I am trying to implement player state machine by following Finite State Machine in Godot by Nathan Lovato from GDQuest. However I am not sure how exactly I should write my code. I would really appreciate if someone could teach me how implement player state machine.
Current Player script. Player can move right and left, jump, and double jump.
extends KinematicBody2D
const UP_DIRECTION := Vector2.UP
export var can_move = true
export var speed := 200.0
export var jump_strength := 450
export var maximum_jumps := 2
export var double_jump_strength := 400
export var gravity := 1200
var _jumps_made := 0
var _velocity := Vector2.ZERO
onready var position2D = $Position2D
onready var _animation_player: AnimationPlayer = $Position2D/PlayerSkinIK/AnimationPlayer
func _physics_process(delta: float) -> void:
#Left and Right Movement Direction
var _horizontal_direction = (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)
#X and Y velocity
_velocity.x = _horizontal_direction * speed
_velocity.y += gravity * delta
#Player State
var is_falling := _velocity.y > 0.0 and not is_on_floor()
var is_jumping := Input.is_action_just_pressed("jump") and is_on_floor()
var is_double_jumping := Input.is_action_just_pressed("jump") and is_falling
var is_jump_cancelled := Input.is_action_just_released("jump") and _velocity.y < 0.0
var is_idling := is_on_floor() and is_zero_approx(_velocity.x)
var is_running := is_on_floor() and not is_zero_approx(_velocity.x)
#Jump Counter
if is_jumping:
_jumps_made += 1
_velocity.y = -jump_strength
elif is_double_jumping:
_jumps_made += 1
if _jumps_made <= maximum_jumps:
_velocity.y = -double_jump_strength
elif is_jump_cancelled:
_velocity.y = 0.0
elif is_idling or is_running:
_jumps_made = 0
if (can_move == true):
#Velocity Calculation
_velocity = move_and_slide(_velocity, UP_DIRECTION)
#Flip Sprite
if get_global_mouse_position().x > $Position2D/PlayerSkinIK.global_position.x:
position2D.scale.x=1
else:
position2D.scale.x=-1
#Play WalkForward animation when moving towards mouse, play WalkBackward animation when moving away from mouse
if position2D.scale.x==1:
if Input.is_action_pressed("move_right"):
print("forward1")
if is_on_floor():
_animation_player.play("Player-Run IK")
elif Input.is_action_pressed("move_left"):
print("backward1")
if is_on_floor():
_animation_player.play("Player-Run IK Backward")
elif position2D.scale.x==-1:
if Input.is_action_pressed("move_left"):
print("forward2")
if is_on_floor():
_animation_player.play("Player-Run IK")
elif Input.is_action_pressed("move_right"):
print("backward2")
if is_on_floor():
_animation_player.play("Player-Run IK")
#Animation Control
if is_jumping or is_double_jumping:
_animation_player.play("Player-Jump IK")
elif is_falling:
_animation_player.play("Player-Fall IK")
elif is_idling:
_animation_player.play("Player-Idle IK")
Player Script:
extends KinematicBody2D
class_name Player
enum States {ON_GROUND, IN_AIR, GLIDING}
var _state : int = States.ON_GROUND
export var speed := 200.0
var _velocity := Vector2.ZERO
var glide_gravity := 1000.0
var base_gravity := 4000.0
var glide_acceleration := 1000.0
var glide_max_speed := 1000.0
var glide_jump_impulse := 500.0
var jump_impulse := 1200.0
func _physics_process(delta: float) -> void:
var input_direction_x: float = (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)
var is_jumping: bool = _state == States.ON_GROUND and Input.is_action_pressed("jump")
if Input.is_action_just_pressed("glide") and _state == States.IN_AIR:
_state = States.GLIDING
# Canceling gliding.
if _state == States.GLIDING and Input.is_action_just_pressed("move_up"):
_state = States.IN_AIR
# Calculating horizontal velocity.
if _state == States.GLIDING:
_velocity.x += input_direction_x * glide_acceleration * delta
_velocity.x = min(_velocity.x, glide_max_speed)
else:
_velocity.x = input_direction_x * speed
# Calculating vertical velocity.
var gravity := glide_gravity if _state == States.GLIDING else base_gravity
_velocity.y += gravity * delta
if is_jumping:
var impulse = glide_jump_impulse if _state == States.GLIDING else jump_impulse
_velocity.y = -jump_impulse
_state = States.IN_AIR
# Moving the character.
_velocity = move_and_slide(_velocity, Vector2.UP)
# If we're gliding and we collide with something, we turn gliding off and the character falls.
if _state == States.GLIDING and get_slide_count() > 0:
_state = States.IN_AIR
if is_on_floor():
_state = States.ON_GROUND
func change_state(new_state: int) -> void:
var previous_state := _state
_state = new_state
Player Idle:
extends PlayerState
func enter(_msg := {}) -> void:
player.velocity = Vector2.ZERO
func physics_update(_delta: float) -> void:
if not player.is_on_floor():
state_machine.transition_to("Air")
return
if Input.is_action_pressed("jump"):
state_machine.transition_to("Air", {do_jump = true})
elif Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"):
state_machine.transition_to("Move")
Player Move:
extends PlayerState
func physics_update(delta: float) -> void:
if not player.is_on_floor():
state_machine.transition_to("Air")
return
var input_direction_x: float =(
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)
player.velocity.x = player.speed * input_direction_x
player.velocity.y += player.graivty * delta
player.velocity = player.move_and_slide(player.velocity, Vector2.UP)
if Input.is_action_pressed("jump"):
state_machine.transition_to("Air", {do_jump = true})
elif is_equal_approx(input_direction_x, 0.0):
state_machine.transition_to("Idle")
Player State:
extends State
class_name PlayerState
var player: Player
func _ready() -> void:
yield(owner, "ready")
player = owner as Player
assert(player != null)
State:
# Virtual base class for all states.
class_name State
extends Node
# Reference to the state machine, to call its `transition_to()` method directly.
# That's one unorthodox detail of our state implementation, as it adds a dependency between the
# state and the state machine objects, but we found it to be most efficient for our needs.
# The state machine node will set it.
var state_machine = null
# Virtual function. Receives events from the `_unhandled_input()` callback.
func handle_input(_event: InputEvent) -> void:
pass
# Virtual function. Corresponds to the `_process()` callback.
func update(_delta: float) -> void:
pass
# Virtual function. Corresponds to the `_physics_process()` callback.
func physics_update(_delta: float) -> void:
pass
# Virtual function. Called by the state machine upon changing the active state. The `msg` parameter
# is a dictionary with arbitrary data the state can use to initialize itself.
func enter(_msg := {}) -> void:
pass
# Virtual function. Called by the state machine before changing the active state. Use this function
# to clean up the state.
func exit() -> void:
pass
State Machine:
# Generic state machine. Initializes states and delegates engine callbacks
# (_physics_process, _unhandled_input) to the active state.
class_name StateMachine
extends Node
# Emitted when transitioning to a new state.
signal transitioned(state_name)
# Path to the initial active state. We export it to be able to pick the initial state in the inspector.
export var initial_state := NodePath()
# The current active state. At the start of the game, we get the `initial_state`.
onready var state: State = get_node(initial_state)
func _ready() -> void:
yield(owner, "ready")
# The state machine assigns itself to the State objects' state_machine property.
for child in get_children():
child.state_machine = self
state.enter()
# The state machine subscribes to node callbacks and delegates them to the state objects.
func _unhandled_input(event: InputEvent) -> void:
state.handle_input(event)
func _process(delta: float) -> void:
state.update(delta)
func _physics_process(delta: float) -> void:
state.physics_update(delta)
# This function calls the current state's exit() function, then changes the active state,
# and calls its enter function.
# It optionally takes a `msg` dictionary to pass to the next state's enter() function.
func transition_to(target_state_name: String, msg: Dictionary = {}) -> void:
# Safety check, you could use an assert() here to report an error if the state name is incorrect.
# We don't use an assert here to help with code reuse. If you reuse a state in different state machines
# but you don't want them all, they won't be able to transition to states that aren't in the scene tree.
if not has_node(target_state_name):
return
state.exit()
state = get_node(target_state_name)
state.enter(msg)
emit_signal("transitioned", state.name)
Most of the time I start like this.
I hope you will find it helpful.
enum States {IDLE, MOVE, JUMP}
var current_state = States.IDLE
func _state_logic():
match current_state:
States.IDLE:
# STATE TRANSITION: IDLE -- MOVE
if Input.is_aciton_pressed("move_player"):
current_state = State.MOVE
# STATE TRANSITION: IDLE -- JUMP
if Input.is_aciton_pressed("move_player"):
current_state = State.JUMP
States.MOVE:
movement_direction = input_direction
# STATE TRANSITION: IDLE -- JUMP
if Input.is_aciton_pressed("move_player"):
current_state = State.JUMP
# STATE TRANSITION: MOVE -- IDLE
if Input.is_action_released("move_player"):
current_state = State.IDLE
States.JUMP:
handle_player_jump()
# OTHER STATE TRANSITION CODE

How to translate a vector from an offset vector

How to translate a vector from an offset vector.
I have a line with 2 points attached one on the line start and one on the end vector of the line.
I want to translate the line and the points base on the DragConstrols position and not base on the line position I am just updating the line geometry vertices and the points position base on the line start and end vectors.
Keap in mind the points are not childs of the line, all objects are childs of the Scene
The line stores the points inside the userData of the line.
My goal is to translate all this 3 objects from the drag offset Vector3
Here is the screenshots of the line with the points.
start_vector = (-140, 60, 0)
end_vector = (-70, 100, 0)
I know the line start vector before the user drags the line v1 = (-140, 60, 0) and I
know the line end vector before the drag v2 = (-70, 100, 0)
Now when the user drags the line with the DragConstrols I dont let the dragControls to change the line position I get the dragConstrol vector3 and I want to translate the line vetrices base on that vector.
The DragControls allways start from (0, 0, 0) so I try to add the line vectors (v1, v2) with the position but I get this result.
Can someone show me how to set the line start and end vector from the dragControls position.
Or how to translate vectors from offset vectors.
Thank you.
My code so far.
Keep in mind the line is a Line2 (fat line) three/examples/jsm/lines/Line2
class NewLine extends Line2 {
private _start = new Vector3();
private _end = new Vector3();
/** The viewport width */
public viewportWidth: number;
/** The viewport height */
public viewportHeight: number;
/** The line3 */
public line3 = new Line3();
public get start() { return this._start; }
public set start(v: Vector3) {
this._start = v;
this.line3.set(v, this._end);
this.geometry['setPositions']([
v.x,
v.y,
v.z,
this._end.x,
this._end.y,
this._end.z
]);
this.geometry['verticesNeedUpdate'] = true; // maybe i don't need that
this.geometry.computeBoundingSphere();
this.computeLineDistances();
}
public get end() { return this._end; }
public set end(v: Vector3) {
this._end = v;
this.line3.set(this._start, v);
this.geometry['setPositions']([
this._start.x,
this._start.y,
this._start.z,
v.x,
v.y,
v.z,
]);
this.geometry['verticesNeedUpdate'] = true; // maybe i don't need that
this.geometry.computeBoundingSphere();
this.computeLineDistances();
}
constructor( start: Vector3, end: Vector3 ) {
super();
// create geometry
const geometry = new LineGeometry();
geometry.setPositions([
start.x, start.y, start.z,
end.x, end.y, end.z
]);
this.geometry = geometry;
// create material
const material = new LineMaterial({
color: 0x000000,
linewidth:5,
depthTest: false, // new
vertexColors: false,
});
this.material = material;
this.computeLineDistances();
this.scale.set( 1, 1, 1 );
this.line3.set(start, end);
}
}
/**
* Translates a line base on DragControls position.
* line: The NewLine
* position: The new Vector3 position from dragControls
*/
translateLineFromVector(line: NewLine, position: Vector3) {
const v1 = line.start;
const v2 = line.end;
const offset_start = line.start.clone().add( position ).sub(line.position);
const offset_end = line.end.clone().add( position ).sub(line.position);
line.start.copy( offset_start );
line.end.copy( offset_end );
}
Standard approach for dragging:
On mouse down(X,Y):
If Clicked at object and not Dragging:
Dragging = True
X0 = X
Y0 = Y
//important - remember object coordinates at this moment!
V1_0 = V1
V2_0 = V2
On mouse moving (X,Y) :
If Dragging:
//Draw object at coordinates shifted by (X - X0), (Y - Y0)
//from initial position, not from the current one
V1.X = V1_0.X + X - X0
V1.Y = V1_0.Y + Y - Y0
same for V2
DrawObject(V1,V2)
On Mouse Up(X,Y):
If Dragging:
Fragging = False
Fix positions V1,V2 if needed

Why isn't my code working for basic Movement in Godot?

I just started a new project in godot. So far I have one png which is the main player. I have added a sprite and CollisionShape2D to the player. When i run the game the player does not move (using keys). Does anyone know what is going on here? Heres my code:
extends KinematicBody2D
export var speed = 10.0
export var tileSize = 32.0
var initpos = Vector2()
var dir = Vector2()
var facing = 'down'
var counter = 0.0
var moving = false
func _ready():
initpos = position
func _process(delta):
if not moving:
set_dir()
elif dir != Vector2():
move(delta)
else:
moving = false
func set_dir(): #set moving
dir = get_dir()
if dir.x != 0 or dir.y != 0:
moving = true
initpos = position
func get_dir(): #user input
var x = 0
var y = 0
if dir.y == 0:
x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
if dir.x == 0:
y = int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
return Vector2(x, y)
func move(delta): #move player linearly
counter += delta + speed
if counter >= 1.0:
position = initpos + dir * tileSize
counter = 0.0
moving = false
else:
position = initpos + dir * tileSize * counter
EDIT:
I have also tried copying and pasting the code directly from Godots documentation and the player still does not move:
extends KinematicBody2D
export (int) var speed = 200
var velocity = Vector2()
func get_input():
velocity = Vector2()
if Input.is_action_pressed('right'):
velocity.x += 1
if Input.is_action_pressed('left'):
velocity.x -= 1
if Input.is_action_pressed('down'):
velocity.y += 1
if Input.is_action_pressed('up'):
velocity.y -= 1
velocity = velocity.normalized() * speed
func _physics_process(delta):
get_input()
velocity = move_and_slide(velocity)
I figured it out. When adding a new script I did not add it to the Player but to the scene.

Find where line-segments intersect with a box

I am trying to figure out where a bunch of line-segments clip into a window around them. I saw the Liang–Barsky algorithm, but that seems to assume the segments already clip the edges of the window, which these do not.
Say I have a window from (0,0) to (26,16), and the following segments:
(7,6) - (16,3)
(10,6) - (19,6)
(13,10) - (21,3)
(16,12) - (19,14)
Illustration:
I imagine I need to extend the segments to a certain X or Y point, till they hit the edge of the window, but I don't know how.
How would I find the points where these segments (converted to lines?) clip into the edge of the window? I will be implementing this in C#, but this is pretty language-agnostic.
If you have two line segments P and Q with points
P0 - P1
Q0 - Q1
The line equations are
P = P0 + t(P1 - P0)
Q = Q0 + r(Q1 - Q0)
then to find out where they intersect after extension you need to solve the following equation for t and r
P0 + t(P1 - P0) = Q0 + r(Q1 - Q0)
The following code can do this. ( Extracted from my own code base )
public static (double t, double r )? SolveIntersect(this Segment2D P, Segment2D Q)
{
// a-d are the entries of a 2x2 matrix
var a = P.P1.X - P.P0.X;
var b = -Q.P1.X + Q.P0.X;
var c = P.P1.Y - P.P0.Y;
var d = -Q.P1.Y + Q.P0.Y;
var det = a*d - b*c;
if (Math.Abs( det ) < Utility.ZERO_TOLERANCE)
return null;
var x = Q.P0.X - P.P0.X;
var y = Q.P0.Y - P.P0.Y;
var t = 1/det*(d*x - b*y);
var r = 1/det*(-c*x + a*y);
return (t, r);
}
If null is returned from the function then it means the lines are parallel and cannot intersect. If a result is returned then you can do.
var result = SolveIntersect( P, Q );
if (result != null)
{
var ( t, r) = result.Value;
var p = P.P0 + t * (P.P1 - P.P0);
var q = Q.P0 + t * (Q.P1 - Q.P0);
// p and q are the same point of course
}
The extended line segments will generally intersect more than one box edge but only one of those intersections will be inside the box. You can check this easily.
bool IsInBox(Point corner0, Point corner1, Point test) =>
(test.X > corner0.X && test.X < corner1.X && test.Y > corner0.Y && test.Y < corner1.Y ;
That should give you all you need to extend you lines to the edge of your box.
I managed to figure this out.
I can extend my lines to the edge of the box by first finding the equations of my lines, then solving for the X and Y of each of the sides to get their corresponding point. This requires passing the max and min Y and the max and min X into the following functions, returning 4 values. If the point is outside the bounds of the box, it can be ignored.
My code is in C#, and is making extension methods for EMGU's LineSegment2D. This is a .NET wrapper for OpenCv.
My Code:
public static float GetYIntersection(this LineSegment2D line, float x)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if(dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return m * x + b;
}
public static float GetXIntersection(this LineSegment2D line, float y)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if (dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return (y - b) / m;
}
I can then take these points, check if they are in the bounds of the box, discard the ones that are not, remove duplicate points (line goes directly into corner). This will leave me with one x and one y value, which I can then pair to the corresponding min or max Y or X values I passed into the functions to make 2 points. I can then make my new segment with the two points.
Wiki description of Liang-Barsky algorithm is not bad, but code is flaw.
Note: this algorithm intended to throw out lines without intersection as soon as possible. If most of lines intersect the rectangle, then approach from your answer might be rather effective, otherwise L-B algorithm wins.
This page describes approach in details and contains concise effective code:
// Liang-Barsky function by Daniel White # http://www.skytopia.com/project/articles/compsci/clipping.html
// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all).
//
bool LiangBarsky (double edgeLeft, double edgeRight, double edgeBottom, double edgeTop, // Define the x/y clipping values for the border.
double x0src, double y0src, double x1src, double y1src, // Define the start and end points of the line.
double &x0clip, double &y0clip, double &x1clip, double &y1clip) // The output values, so declare these outside.
{
double t0 = 0.0; double t1 = 1.0;
double xdelta = x1src-x0src;
double ydelta = y1src-y0src;
double p,q,r;
for(int edge=0; edge<4; edge++) { // Traverse through left, right, bottom, top edges.
if (edge==0) { p = -xdelta; q = -(edgeLeft-x0src); }
if (edge==1) { p = xdelta; q = (edgeRight-x0src); }
if (edge==2) { p = -ydelta; q = -(edgeBottom-y0src);}
if (edge==3) { p = ydelta; q = (edgeTop-y0src); }
if(p==0 && q<0) return false; // Don't draw line at all. (parallel line outside)
r = q/p;
if(p<0) {
if(r>t1) return false; // Don't draw line at all.
else if(r>t0) t0=r; // Line is clipped!
} else if(p>0) {
if(r<t0) return false; // Don't draw line at all.
else if(r<t1) t1=r; // Line is clipped!
}
}
x0clip = x0src + t0*xdelta;
y0clip = y0src + t0*ydelta;
x1clip = x0src + t1*xdelta;
y1clip = y0src + t1*ydelta;
return true; // (clipped) line is drawn
}

Entity look at for cameras and objects

I'm currently extending my 3D-engine for a better entity system which includes cameras. This allows me to put cameras into parent entities which may also be in another entity ( and so on... ).
--Entity
----Entity
------Camera
Now i want to set the cameras look direction, i'm doing this with the following method which is also used to set the look at of a entity:
public void LookAt(Vector3 target, Vector3 up)
{
Matrix4x4 oldValue = _Matrix;
_Matrix = Matrix4x4.LookAt(_Position, target, up) * Matrix4x4.Scale(_Scale);
Vector3 p, s;
Quaternion r;
Quaternion oldRotation = _Rotation;
_Matrix.Decompose(out p, out s, out r);
_Rotation = r;
// Update dependency properties
ForceUpdate(RotationDeclaration, _Rotation, oldRotation);
ForceUpdate(MatrixDeclaration, _Matrix, oldValue);
}
But the code is only working for cameras and not for other entities, when using this method for other entities the object is rotating at it's position ( The entity is at a root node, so it has no parent ). The matrix's look at method looks like this:
public static Matrix4x4 LookAt(Vector3 position, Vector3 target, Vector3 up)
{
// Calculate and normalize forward vector
Vector3 forward = position - target;
forward.Normalize();
// Calculate and normalie side vector ( side = forward x up )
Vector3 side = Vector3.Cross(up, forward);
side.Normalize();
// Recompute up as: up = side x forward
up = Vector3.Cross(forward, side);
up.Normalize();
//------------------
Matrix4x4 result = new Matrix4x4(false)
{
M11 = side.X,
M21 = side.Y,
M31 = side.Z,
M41 = 0,
M12 = up.X,
M22 = up.Y,
M32 = up.Z,
M42 = 0,
M13 = forward.X,
M23 = forward.Y,
M33 = forward.Z,
M43 = 0,
M14 = 0,
M24 = 0,
M34 = 0,
M44 = 1
};
result.Multiply(Matrix4x4.Translation(-position.X, -position.Y, -position.Z));
return result;
}
The decompose method also returns the wrong value for the position variable p. So why is the camera working and the entity not?
I had a similar problem a while ago.The problem is that camera transformations are different from others as those are negated.What I did was first to transform camera eye, center and up into world space (transforming those vectors by its parent model matrix )and then calculating the lookAt() .That is how it worked for me.

Resources