ursina_view.py 10.6 KB
Newer Older
三月三net's avatar
三月三net 已提交
1 2 3 4 5 6 7 8 9
# -*- coding:utf-8 -*-
# title           :ursina天体视图
# description     :ursina天体视图(天体效果展示用,需要安装 ursina)
# author          :Python超人
# date            :2023-02-11
# link            :https://gitcode.net/pythoncr/
# python_version  :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
三月三net's avatar
三月三net 已提交
10
from ursina import Ursina, window, Entity, Mesh, SmoothFollow, Texture, clamp, time, \
三月三net's avatar
三月三net 已提交
11
    camera, color, mouse, Vec2, Vec3, Vec4, \
三月三net's avatar
三月三net 已提交
12
    load_texture, held_keys, destroy
M
march3 已提交
13

M
march3 已提交
14 15
from ursina.prefabs.first_person_controller import FirstPersonController
import sys
三月三net's avatar
三月三net 已提交
16
from bodies import Body
三月三net's avatar
三月三net 已提交
17

三月三net's avatar
三月三net 已提交
18 19
from simulators.ursina.ursina_config import UrsinaConfig
from common.color_utils import adjust_brightness, conv_to_vec4_color
三月三net's avatar
三月三net 已提交
20
from simulators.views.body_view import BodyView
三月三net's avatar
三月三net 已提交
21
from simulators.views.ursina_mesh import create_sphere, create_torus
三月三net's avatar
三月三net 已提交
22
import numpy as np
M
march3 已提交
23
import math
三月三net's avatar
三月三net 已提交
24

M
march3 已提交
25

三月三net's avatar
三月三net 已提交
26 27
class UrsinaPlayer(FirstPersonController):
    """
M
march3 已提交
28

三月三net's avatar
三月三net 已提交
29
    """
三月三net's avatar
三月三net 已提交
30 31
    body_rotation_speed_control = 1.0

M
march3 已提交
32
    def __init__(self, position, targets=None):
M
march3 已提交
33
        super().__init__()
三月三net's avatar
三月三net 已提交
34 35
        # camera.fov = 2000 # 100
        # camera.rotation_y = 90
M
march3 已提交
36
        self.planets = None
M
march3 已提交
37
        if targets is not None:
M
march3 已提交
38
            self.planets = []
M
march3 已提交
39
            # targets = [view.planet.parent for view in targets]
M
march3 已提交
40
            # targets_parent = Entity()
M
march3 已提交
41
            for view in targets:
M
march3 已提交
42 43 44 45 46 47
                # view.planet.parent = targets_parent
                self.planets.append(view.planet)
            # self.camera_adj(planets)
            #     # planets.append(view.planet)
            #
            # camera.add_script(SmoothFollow(targets_parent, offset=(0, 8, -20)))
三月三net's avatar
三月三net 已提交
48
        pos = np.array(position) * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
49
        # self.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
50
        # 将摄像机位置设置为 x=0、y=1、z=0 的位置
三月三net's avatar
三月三net 已提交
51
        camera.position = Vec3(pos[0], pos[1], pos[2])
三月三net's avatar
三月三net 已提交
52
        # self.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
53
        # 将摄像机的观察角度绕 x 轴旋转 45 度,绕 y 轴旋转 0 度,绕 z 轴旋转 0 度
三月三net's avatar
三月三net 已提交
54 55
        # camera.rotation = Vec3(45, 90, 0)
        camera.rotation = Vec3(0, 0, 0)
M
march3 已提交
56

三月三net's avatar
三月三net 已提交
57 58 59 60
        # self.gravity = 0
        # self.vspeed = 400
        # self.speed = 1000
        # self.mouse_sensitivity = Vec2(160, 160)
M
march3 已提交
61
        self.on_enable()
三月三net's avatar
三月三net 已提交
62
        # self.rotation_speed = 80
M
march3 已提交
63

M
march3 已提交
64 65 66 67 68 69
    def input(self, key):
        if key == "escape":
            if mouse.locked:
                self.on_disable()
            else:
                sys.exit()
M
march3 已提交
70
        return super().input(key)
M
march3 已提交
71

三月三net's avatar
三月三net 已提交
72 73

class Planet(Entity):
三月三net's avatar
三月三net 已提交
74

M
march3 已提交
75 76
    def __init__(self, body_view: BodyView):
        self.body_view = body_view
三月三net's avatar
三月三net 已提交
77
        self.rotation_speed = self.body_view.body.rotation_speed
M
march3 已提交
78 79 80
        self.rotMode = 'x'  # random.choice(["x", "y", "z"])
        self.name = body_view.name

三月三net's avatar
三月三net 已提交
81 82
        pos = body_view.position * body_view.body.distance_scale * UrsinaConfig.SCALE_FACTOR
        scale = body_view.body.diameter * body_view.body.size_scale * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
83

三月三net's avatar
三月三net 已提交
84
        if hasattr(body_view, "texture"):
三月三net's avatar
三月三net 已提交
85 86 87
            texture = load_texture(body_view.texture)
        else:
            texture = None
M
march3 已提交
88

三月三net's avatar
三月三net 已提交
89
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
90
            # 创建一个星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
91
            model = create_torus(0.86, 1.02, 64, 4)
三月三net's avatar
三月三net 已提交
92 93
            rotation = (90, 0, 0)
        else:
三月三net's avatar
三月三net 已提交
94 95
            # 创建一个天体
            model = create_sphere(0.5, 32)
三月三net's avatar
三月三net 已提交
96 97
            rotation = (0, 0, 0)

三月三net's avatar
三月三net 已提交
98
        super().__init__(
三月三net's avatar
三月三net 已提交
99
            # model="sphere",
三月三net's avatar
三月三net 已提交
100
            model=model,
三月三net's avatar
三月三net 已提交
101 102 103 104
            scale=scale,
            texture=texture,
            color=color.white,
            position=pos,
三月三net's avatar
三月三net 已提交
105
            rotation=rotation,
三月三net's avatar
三月三net 已提交
106
            double_sided=True)
三月三net's avatar
三月三net 已提交
107

三月三net's avatar
三月三net 已提交
108
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
109
            # 星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
110
            self.set_light_off()
三月三net's avatar
三月三net 已提交
111 112
        else:
            # 一个天体
三月三net's avatar
三月三net 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
            # 拖尾球体的初始化
            self.trail_init()

    def trail_init(self):
        """
        拖尾球体的初始化
        :return:
        """
        # 存放拖尾球体
        self.trails = {}
        # 拖尾球体的数量
        self.trail_len = 100

        # 根据天体的颜色获取拖尾的颜色
        trail_color = conv_to_vec4_color(self.body_view.body.color)
        trail_color = adjust_brightness(trail_color, 0.4)
        self.trail_color = color.rgba(trail_color[0], trail_color[1], trail_color[2], 0.6)
        # 拖尾球体的大小为该天体的 1/5
        self.trail_scale = self.scale_x / 5
        if self.trail_scale < 1:
            # 如果太小,则
            pass
三月三net's avatar
三月三net 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147

    def distance_between_two_points(self, point_a: Vec3, point_b: Vec3) -> float:
        # 计算两点在 x、y、z 三个坐标轴上的差值
        diff_x = point_a.x - point_b.x
        diff_y = point_a.y - point_b.y
        diff_z = point_a.z - point_b.z

        # 计算两点之间的距离
        distance = math.sqrt(diff_x ** 2 + diff_y ** 2 + diff_z ** 2)

        return distance

    def create_trails(self):
三月三net's avatar
三月三net 已提交
148 149 150 151 152 153 154 155
        """
        创建拖尾
        :return:
        """
        # 当前天体的位置
        try:
            pos = self.position
        except Exception as e:
三月三net's avatar
三月三net 已提交
156
            self.destroy_all()
三月三net's avatar
三月三net 已提交
157
            return
三月三net's avatar
三月三net 已提交
158
        trails_keys = self.trails.keys()
三月三net's avatar
三月三net 已提交
159
        # 如果有拖尾
三月三net's avatar
三月三net 已提交
160
        if len(trails_keys) > 0:
三月三net's avatar
三月三net 已提交
161
            # 获取最后一个拖尾的位置
三月三net's avatar
三月三net 已提交
162 163
            last_key = list(trails_keys)[-1]
            last_pos = self.trails[last_key]
三月三net's avatar
三月三net 已提交
164
            # 获取拖尾与当前天体的位置
三月三net's avatar
三月三net 已提交
165
            distance = self.distance_between_two_points(pos, last_pos)
三月三net's avatar
三月三net 已提交
166 167
            # 如果位置比较近,就不创建拖尾了,保证拖尾间隔一定的距离
            if distance < self.trail_scale * 1.2:  # 间隔距离不小于1.2倍的拖尾球体
三月三net's avatar
三月三net 已提交
168 169
                return

三月三net's avatar
三月三net 已提交
170
        # 创建拖尾球体,并作为字典的key,存放拖尾球体的位置
三月三net's avatar
三月三net 已提交
171
        self.trails[self.create_trail(pos)] = pos
三月三net's avatar
三月三net 已提交
172 173 174 175 176 177

        # 计算拖尾球体超过的数量
        trail_overflow_count = len(self.trails) - self.trail_len

        if trail_overflow_count > 0:
            # 如果拖尾球体超过的数量,就删除之前的拖尾球体
三月三net's avatar
三月三net 已提交
178 179
            for entity, pos in self.trails.items():
                destroy(entity)
三月三net's avatar
三月三net 已提交
180 181
                trail_overflow_count -= 1
                if trail_overflow_count <= 0:
三月三net's avatar
三月三net 已提交
182 183
                    break

三月三net's avatar
三月三net 已提交
184
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
185 186 187 188 189 190
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
        trail = Entity(model="sphere", color=self.trail_color, scale=self.trail_scale, position=pos)
三月三net's avatar
三月三net 已提交
191 192
        trail.set_light_off()
        return trail
三月三net's avatar
三月三net 已提交
193

M
march3 已提交
194
    def turn(self):
三月三net's avatar
三月三net 已提交
195
        pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
196 197 198 199 200 201
        if self.body_view.body.parent is None:
            self.x = -pos[1]
            self.y = pos[2]
            self.z = pos[0]
        else:
            self.follow_parent()
M
march3 已提交
202

三月三net's avatar
三月三net 已提交
203 204 205 206 207 208 209 210
        dt = 0
        if hasattr(self.body_view.body, "dt"):
            dt = self.body_view.body.dt
        if self.rotation_speed is None or dt == 0:
            self.rotspeed = 0
            # 旋转速度和大小成反比(未使用真实数据)
            # self.rotspeed = 30000 / self.body_view.raduis  # random.uniform(1.0, 2.0)
        else:
三月三net's avatar
三月三net 已提交
211
            # 是通过月球保持一面面对地球,调整得到
三月三net's avatar
三月三net 已提交
212 213
            self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
                            UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
三月三net's avatar
三月三net 已提交
214 215
            # rotation_speed 度/小时  dt 秒 = (dt / 3600)小时

三月三net's avatar
三月三net 已提交
216 217 218 219 220 221
        # if self.rotation_y < 0:
        #     self.rotation_y += 360
        try:
            # 天体旋转
            self.rotation_y -= self.rotspeed
        except Exception as e:
三月三net's avatar
三月三net 已提交
222
            self.destroy_all()
三月三net's avatar
三月三net 已提交
223 224 225 226 227 228 229 230
            return

        # 如果有行星环
        if hasattr(self, "ring"):
            # 如果有行星环,则不让行星环跟随行星转动
            self.ring.rotation = -Vec3(self.rotation_x - self.ring_rotation_x,
                                       self.rotation_y,
                                       self.rotation_z)
M
march3 已提交
231

三月三net's avatar
三月三net 已提交
232 233 234
        # 有时候第一个位置不正确,所以判断一下有历史记录后在创建
        if len(self.body_view.body.his_position()) > 1:
            self.create_trails()
三月三net's avatar
三月三net 已提交
235

三月三net's avatar
三月三net 已提交
236 237 238 239 240 241 242 243 244 245 246 247

    def follow_parent(self):
        if not hasattr(self.body_view, "bodies_system"):
            return
        sys = self.body_view.bodies_system
        for b in sys.bodies:
            if self.body_view.body.parent == b:
                pos = b.position * UrsinaConfig.SCALE_FACTOR
                self.x = -pos[1]
                self.y = pos[2]
                self.z = pos[0]

M
march3 已提交
248
    def create_rings(self):
三月三net's avatar
三月三net 已提交
249
        """
三月三net's avatar
三月三net 已提交
250
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
251 252
        :return:
        """
三月三net's avatar
三月三net 已提交
253
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
254
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
255
        # 创建行星环
三月三net's avatar
三月三net 已提交
256 257 258
        # self.ring = Entity(parent=self.planet, model='circle', texture='../textures/saturnRings.jpg', scale=3.5,
        #                    rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

三月三net's avatar
三月三net 已提交
259
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
260
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
261
        # 创建行星环
三月三net's avatar
三月三net 已提交
262
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
263
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
264 265 266
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

        # 设置行星环不受灯光影响,否则看不清行星环
三月三net's avatar
三月三net 已提交
267
        self.ring.set_light_off()
三月三net's avatar
三月三net 已提交
268

三月三net's avatar
三月三net 已提交
269 270 271 272 273
    def destroy_all(self):
        # 从天体系统中移除自己(TODO:暂时还不能移除)
        # self.body_view.bodies_system.bodies.remove(self.body_view.body)

        # 删除拖尾
三月三net's avatar
三月三net 已提交
274 275
        for entity, pos in self.trails.items():
            destroy(entity)
三月三net's avatar
三月三net 已提交
276
        # 如果有行星环,则删除行星环
三月三net's avatar
三月三net 已提交
277 278
        if hasattr(self, "ring"):
            destroy(self.ring)
三月三net's avatar
三月三net 已提交
279
        # 最后删除自己
三月三net's avatar
三月三net 已提交
280 281 282 283 284 285 286 287
        destroy(self)


class UrsinaView(BodyView):
    """
    ursina天体视图(天体效果展示用)
    """

三月三net's avatar
三月三net 已提交
288 289
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
290 291 292 293 294 295 296
        self.velocity = body.velocity

        self.planet = Planet(self)
        if body.has_rings:
            # 创建行星环(目前只有土星环)
            self.planet.create_rings()

三月三net's avatar
三月三net 已提交
297
    def update(self):
三月三net's avatar
三月三net 已提交
298
        """
三月三net's avatar
三月三net 已提交
299

三月三net's avatar
三月三net 已提交
300 301 302
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
303

三月三net's avatar
三月三net 已提交
304 305 306 307
    def appear(self):
        pass

    def disappear(self):
三月三net's avatar
三月三net 已提交
308 309
        self.planet.destroy_all()
        self.appeared = False