From d05d4a33fa21d46a0acc96c1c7962f323299e861 Mon Sep 17 00:00:00 2001 From: Tipragot Date: Sun, 7 Jan 2024 10:48:49 +0100 Subject: [PATCH] Save unfinished work --- src/plugins/physics.py | 408 ++++++++++++++++++++--------------------- 1 file changed, 204 insertions(+), 204 deletions(-) diff --git a/src/plugins/physics.py b/src/plugins/physics.py index 86d15c9..305aed2 100644 --- a/src/plugins/physics.py +++ b/src/plugins/physics.py @@ -1,204 +1,204 @@ -""" -Plugin implémentant une physique exacte pour des collisions AABB. -""" - -from typing import Callable -from engine import GlobalPlugin -from engine.ecs import Entity, World -from engine.math import Vec2 -from plugins.render import Origin, Position, Scale -from plugins.timing import Delta - - -class Solid: - """ - Composant représentant un objet (de préférence imobille pour que la simulation soit prédictible) qui ne laisse pas passer les objets dynamiques. - """ - - -class Velocity(Vec2): - """ - Composant donnant la vélocité d'un objet. - """ - - -class CollisionHandler: - """ - Composant permettant de traiter les collisions. - """ - - def __init__(self, callback: Callable[[Entity, Entity], bool]): - self.callback = callback - - -class AABB: - """ - Définit une boite. - """ - - def __init__(self, min: Vec2, max: Vec2, entity: Entity): - self.min = min - self.max = max - self.entity = entity - - @staticmethod - def from_entity(entity: Entity): - min = entity[Position] - entity[Origin] * entity[Scale] - return AABB(min, min + entity[Scale], entity) - - def entity_position(self, entity: Entity): - scale = self.max - self.min - entity[Position] = self.min + entity[Origin] * scale - entity[Scale] = scale - - def __contains__(self, point: Vec2): - return ( - self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y - ) - - def move(self, movement: Vec2): - self.min += movement - self.max += movement - - def collide(self, other: "AABB"): - return ( - self.min.x < other.max.x - and self.max.x > other.min.x - and self.min.y < other.max.y - and self.max.y > other.min.y - ) - - -def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2): - """ - Renvoie la collision entre deux lignes. - """ - if sa.x == ea.x: - sa.x += 0.0001 - if sb.x == eb.x: - sb.x += 0.0001 - if sa.y == ea.y: - sa.y += 0.0001 - if sb.y == eb.y: - sb.y += 0.0001 - - divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) - if divisor == 0: - uA = 0 - else: - uA = ((eb.x - sb.x) * (sa.y - sb.y) - (eb.y - sb.y) * (sa.x - sb.x)) / (divisor) - divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) - if divisor == 0: - uB = 0 - else: - uB = ((ea.x - sa.x) * (sa.y - sb.y) - (ea.y - sa.y) * (sa.x - sb.x)) / (divisor) - if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1: - return ( - Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length - ) - return 1.0 - - -def line_to_aabb(start: Vec2, end: Vec2, aabb: AABB): - """ - Renvoie la collision entre une ligne et une AABB. - """ - left = line_to_line(start, end, aabb.min, Vec2(aabb.min.x, aabb.max.y)) - right = line_to_line(start, end, Vec2(aabb.max.x, aabb.min.y), aabb.max) - bottom = line_to_line(start, end, aabb.min, Vec2(aabb.max.x, aabb.min.y)) - top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max) - t = min([left, right, bottom, top]) - if t == left: - normal = Vec2(-1, 0) - elif t == right: - normal = Vec2(1, 0) - elif t == bottom: - normal = Vec2(0, -1) - elif t == top: - normal = Vec2(0, 1) - else: - normal = Vec2(0, 0) - return t, normal - - -def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2): - """ - Renvoie la collision entre deux AABB. - """ - if moving.collide(static): - return 1.0, Vec2(0, 0) - size = (moving.max - moving.min) / 2 - static = AABB(static.min - size, static.max + size, static.entity) - start_pos = moving.min + size - return line_to_aabb(start_pos, start_pos + movement, static) - - -def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2): - """ - Renvoie la collision entre deux AABB. - """ - t = 1.0 - normal = Vec2(0, 0) - entity = None - for static in statics: - if static.entity == moving.entity: - continue - result = aabb_to_aabb(moving, static, movement) - if result[0] < t: - t = result[0] - normal = result[1] - entity = static.entity - return t, normal, entity - - -def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False): - world = entity.world - aabb = AABB.from_entity(entity) - others = [ - AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin) - ] - counter = 0 - while movement.length > 0.0001 and counter < 50: - t, normal, obstacle = aabb_to_aabbs(aabb, others, movement) - if t == 1.0: - step = movement - else: - step = movement * max(t - 0.000001, 0) - aabb.move(step) - aabb.entity_position(entity) - movement -= step - if normal.x != 0: - movement.x *= -1 - entity[Velocity].x *= -1 - if normal.y != 0: - movement.y *= -1 - entity[Velocity].y *= -1 - movement /= entity[Velocity] - if obstacle is not None and not disable_callback: - if not entity.get( - CollisionHandler, CollisionHandler(lambda e, o: True) - ).callback(entity, obstacle): - break - if not obstacle.get( - CollisionHandler, CollisionHandler(lambda e, o: True) - ).callback(obstacle, entity): - break - movement *= entity[Velocity] - counter += 1 - - -def __apply_velocity(world: World): - """ - Applique la vélocité a toutes les entitées. - """ - delta = world[Delta] - for entity in world.query(Velocity, Position, Scale, Origin): - move_entity(entity, entity[Velocity] * delta) - - -PLUGIN = GlobalPlugin( - [], - [__apply_velocity], - [], - [], -) +""" +Plugin implémentant une physique exacte pour des collisions AABB. +""" + +from typing import Callable +from engine import GlobalPlugin +from engine.ecs import Entity, World +from engine.math import Vec2 +from plugins.render import Origin, Position, Scale +from plugins.timing import Delta + + +class Solid: + """ + Composant représentant un objet (de préférence imobille pour que la simulation soit prédictible) qui ne laisse pas passer les objets dynamiques. + """ + + +class Velocity(Vec2): + """ + Composant donnant la vélocité d'un objet. + """ + + +class CollisionHandler: + """ + Composant permettant de traiter les collisions. + """ + + def __init__(self, callback: Callable[[Entity, Entity], bool]): + self.callback = callback + + +class AABB: + """ + Définit une boite. + """ + + def __init__(self, min: Vec2, max: Vec2, entity: Entity): + self.min = min + self.max = max + self.entity = entity + + @staticmethod + def from_entity(entity: Entity): + min = entity[Position] - entity[Origin] * entity[Scale] + return AABB(min, min + entity[Scale], entity) + + def entity_position(self, entity: Entity): + scale = self.max - self.min + entity[Position] = self.min + entity[Origin] * scale + entity[Scale] = scale + + def __contains__(self, point: Vec2): + return ( + self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y + ) + + def move(self, movement: Vec2): + self.min += movement + self.max += movement + + def collide(self, other: "AABB"): + return ( + self.min.x < other.max.x + and self.max.x > other.min.x + and self.min.y < other.max.y + and self.max.y > other.min.y + ) + + +def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2): + """ + Renvoie la collision entre deux lignes. + """ + if sa.x == ea.x: + sa.x += 0.0001 + if sb.x == eb.x: + sb.x += 0.0001 + if sa.y == ea.y: + sa.y += 0.0001 + if sb.y == eb.y: + sb.y += 0.0001 + + divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) + if divisor == 0: + uA = 0 + else: + uA = ((eb.x - sb.x) * (sa.y - sb.y) - (eb.y - sb.y) * (sa.x - sb.x)) / (divisor) + divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) + if divisor == 0: + uB = 0 + else: + uB = ((ea.x - sa.x) * (sa.y - sb.y) - (ea.y - sa.y) * (sa.x - sb.x)) / (divisor) + if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1: + return ( + Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length + ) + return 1.0 + + +def line_to_aabb(start: Vec2, end: Vec2, aabb: AABB): + """ + Renvoie la collision entre une ligne et une AABB. + """ + left = line_to_line(start, end, aabb.min, Vec2(aabb.min.x, aabb.max.y)) + right = line_to_line(start, end, Vec2(aabb.max.x, aabb.min.y), aabb.max) + bottom = line_to_line(start, end, aabb.min, Vec2(aabb.max.x, aabb.min.y)) + top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max) + t = min([left, right, bottom, top]) + if t == left: + normal = Vec2(-1, 0) + elif t == right: + normal = Vec2(1, 0) + elif t == bottom: + normal = Vec2(0, -1) + elif t == top: + normal = Vec2(0, 1) + else: + normal = Vec2(0, 0) + return t, normal + + +def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2): + """ + Renvoie la collision entre deux AABB. + """ + if moving.collide(static): + return 1.0, Vec2(0, 0) + size = (moving.max - moving.min) / 2 + static = AABB(static.min - size, static.max + size, static.entity) + start_pos = moving.min + size + return line_to_aabb(start_pos, start_pos + movement, static) + + +def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2): + """ + Renvoie la collision entre deux AABB. + """ + t = 1.0 + normal = Vec2(0, 0) + entity = None + for static in statics: + if static.entity == moving.entity: + continue + result = aabb_to_aabb(moving, static, movement) + if result[0] < t: + t = result[0] + normal = result[1] + entity = static.entity + return t, normal, entity + + +def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False): + world = entity.world + aabb = AABB.from_entity(entity) + others = [ + AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin) + ] + counter = 0 + while movement.length > 0.0001 and counter < 50: + t, normal, obstacle = aabb_to_aabbs(aabb, others, movement) + if t == 1.0: + step = movement + else: + step = movement * max(t - 0.000001, 0) + aabb.move(step) + aabb.entity_position(entity) + movement -= step + if normal.x != 0: + movement.x *= -1 + entity[Velocity].x *= -1 + if normal.y != 0: + movement.y *= -1 + entity[Velocity].y *= -1 + movement /= entity[Velocity] + if obstacle is not None and not disable_callback: + if not entity.get( + CollisionHandler, CollisionHandler(lambda e, o: True) + ).callback(entity, obstacle): + break + if not obstacle.get( + CollisionHandler, CollisionHandler(lambda e, o: True) + ).callback(obstacle, entity): + break + movement *= entity[Velocity] + counter += 1 + + +def __apply_velocity(world: World): + """ + Applique la vélocité a toutes les entitées. + """ + delta = world[Delta] + for entity in world.query(Velocity, Position, Scale, Origin): + move_entity(entity, entity[Velocity] * delta) + + +PLUGIN = GlobalPlugin( + [], + [__apply_velocity], + [], + [], +)