ursina_view.py 12.0 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
from simulators.ursina.ursina_config import UrsinaConfig
三月三net's avatar
三月三net 已提交
19
from simulators.ursina.ursina_event import UrsinaEvent
三月三net's avatar
三月三net 已提交
20
from common.color_utils import adjust_brightness, conv_to_vec4_color
三月三net's avatar
三月三net 已提交
21
from simulators.views.body_view import BodyView
三月三net's avatar
三月三net 已提交
22
from simulators.views.ursina_mesh import create_sphere, create_torus
三月三net's avatar
三月三net 已提交
23
import numpy as np
M
march3 已提交
24
import math
三月三net's avatar
三月三net 已提交
25

M
march3 已提交
26

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

三月三net's avatar
三月三net 已提交
30
    """
三月三net's avatar
三月三net 已提交
31

三月三net's avatar
三月三net 已提交
32
    # body_rotation_speed_control = 1.0
三月三net's avatar
三月三net 已提交
33

三月三net's avatar
三月三net 已提交
34
    def __init__(self, position, view_azimuth=0, targets=None):
M
march3 已提交
35
        super().__init__()
三月三net's avatar
三月三net 已提交
36 37
        # camera.fov = 2000 # 100
        # camera.rotation_y = 90
M
march3 已提交
38
        self.planets = None
M
march3 已提交
39
        if targets is not None:
M
march3 已提交
40
            self.planets = []
M
march3 已提交
41
            # targets = [view.planet.parent for view in targets]
M
march3 已提交
42
            # targets_parent = Entity()
M
march3 已提交
43
            for view in targets:
M
march3 已提交
44 45 46 47 48 49
                # 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 已提交
50
        pos = np.array(position) * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
51
        self.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
52
        # 将摄像机位置设置为 x=0、y=1、z=0 的位置
三月三net's avatar
三月三net 已提交
53
        camera.position = Vec3(pos[0], pos[1], pos[2])
三月三net's avatar
三月三net 已提交
54
        # self.x = 90
三月三net's avatar
三月三net 已提交
55
        # self.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
56
        # 将摄像机的观察角度绕 x 轴旋转 45 度,绕 y 轴旋转 0 度,绕 z 轴旋转 0 度
三月三net's avatar
三月三net 已提交
57 58 59 60
        # self.rotation = Vec3(45, 90, 0)
        # camera.look_at(Vec3(0, 0, 0))
        # camera.world_rotation = Vec3(0, 190, 190)
        # camera.enabled = True
三月三net's avatar
三月三net 已提交
61 62 63 64
        # self.gravity = 0
        # self.vspeed = 400
        # self.speed = 1000
        # self.mouse_sensitivity = Vec2(160, 160)
三月三net's avatar
三月三net 已提交
65
        # self.on_enable()
三月三net's avatar
三月三net 已提交
66
        # self.rotation_speed = 80
三月三net's avatar
三月三net 已提交
67 68

        self.on_disable()  # 防止鼠标被窗口锁定
M
march3 已提交
69

三月三net's avatar
三月三net 已提交
70 71 72 73 74 75 76
    # def input(self, key):
    #     if key == "escape":
    #         if mouse.locked:
    #             self.on_disable()
    #         else:
    #             sys.exit()
    #     return super().input(key)
M
march3 已提交
77

三月三net's avatar
三月三net 已提交
78 79

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

三月三net's avatar
三月三net 已提交
81 82
    def on_reset(self):
        # 删除拖尾
三月三net's avatar
三月三net 已提交
83
        self.clear_trails()
三月三net's avatar
三月三net 已提交
84 85
        self.body_view.body.reset()

M
march3 已提交
86 87
    def __init__(self, body_view: BodyView):
        self.body_view = body_view
三月三net's avatar
三月三net 已提交
88
        self.rotation_speed = self.body_view.body.rotation_speed
M
march3 已提交
89 90 91
        self.rotMode = 'x'  # random.choice(["x", "y", "z"])
        self.name = body_view.name

三月三net's avatar
三月三net 已提交
92 93
        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 已提交
94
        self.init_scale = scale
三月三net's avatar
三月三net 已提交
95
        if hasattr(body_view, "texture"):
三月三net's avatar
三月三net 已提交
96 97 98
            texture = load_texture(body_view.texture)
        else:
            texture = None
M
march3 已提交
99

三月三net's avatar
三月三net 已提交
100
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
101
            # 创建一个星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
102
            model = create_torus(0.83, 1.05, 64, 1)
三月三net's avatar
三月三net 已提交
103 104
            rotation = (90, 0, 0)
        else:
三月三net's avatar
三月三net 已提交
105 106
            # 创建一个天体
            model = create_sphere(0.5, 32)
三月三net's avatar
三月三net 已提交
107 108
            rotation = (0, 0, 0)

三月三net's avatar
三月三net 已提交
109 110
        UrsinaEvent.on_reset_subscription(self.on_reset)

三月三net's avatar
三月三net 已提交
111
        super().__init__(
三月三net's avatar
三月三net 已提交
112
            # model="sphere",
三月三net's avatar
三月三net 已提交
113
            model=model,
三月三net's avatar
三月三net 已提交
114 115 116 117
            scale=scale,
            texture=texture,
            color=color.white,
            position=pos,
三月三net's avatar
三月三net 已提交
118
            rotation=rotation  # ,double_sided=True
三月三net's avatar
三月三net 已提交
119
        )
三月三net's avatar
三月三net 已提交
120

三月三net's avatar
三月三net 已提交
121
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
122
            # 星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
123
            self.set_light_off()
三月三net's avatar
三月三net 已提交
124
            self.double_sided = True
三月三net's avatar
三月三net 已提交
125 126
        else:
            # 一个天体
三月三net's avatar
三月三net 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
            # 拖尾球体的初始化
            self.trail_init()

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

        # 根据天体的颜色获取拖尾的颜色
        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 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159

    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 已提交
160 161 162 163 164 165 166 167
        """
        创建拖尾
        :return:
        """
        # 当前天体的位置
        try:
            pos = self.position
        except Exception as e:
三月三net's avatar
三月三net 已提交
168
            print(self.body_view.body)
三月三net's avatar
三月三net 已提交
169
            self.destroy_all()
三月三net's avatar
三月三net 已提交
170
            return
三月三net's avatar
三月三net 已提交
171
        trails_keys = self.trails.keys()
三月三net's avatar
三月三net 已提交
172
        # 如果有拖尾
三月三net's avatar
三月三net 已提交
173
        if len(trails_keys) > 0:
三月三net's avatar
三月三net 已提交
174
            # 获取最后一个拖尾的位置
三月三net's avatar
三月三net 已提交
175 176
            last_key = list(trails_keys)[-1]
            last_pos = self.trails[last_key]
三月三net's avatar
三月三net 已提交
177
            # 获取拖尾与当前天体的位置
三月三net's avatar
三月三net 已提交
178
            distance = self.distance_between_two_points(pos, last_pos)
三月三net's avatar
三月三net 已提交
179 180
            # 如果位置比较近,就不创建拖尾了,保证拖尾间隔一定的距离
            if distance < self.trail_scale * 1.2:  # 间隔距离不小于1.2倍的拖尾球体
三月三net's avatar
三月三net 已提交
181 182
                return

三月三net's avatar
三月三net 已提交
183
        # 创建拖尾球体,并作为字典的key,存放拖尾球体的位置
三月三net's avatar
三月三net 已提交
184
        self.trails[self.create_trail(pos)] = pos
三月三net's avatar
三月三net 已提交
185 186

        # 计算拖尾球体超过的数量
三月三net's avatar
三月三net 已提交
187
        trail_overflow_count = len(self.trails) - UrsinaConfig.trail_length
三月三net's avatar
三月三net 已提交
188 189 190

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

三月三net's avatar
三月三net 已提交
197
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
198 199 200 201 202
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
三月三net's avatar
三月三net 已提交
203 204
        # sphere = create_sphere(1,6)  diamond sphere
        trail = Entity(model='sphere', color=self.trail_color, scale=self.trail_scale, position=pos)
三月三net's avatar
三月三net 已提交
205
        trail.set_light_off()
三月三net's avatar
三月三net 已提交
206 207 208
        # trail.set_color_off()
        # trail.set_color_scale_off()
        # trail.enabled = False
三月三net's avatar
三月三net 已提交
209
        return trail
三月三net's avatar
三月三net 已提交
210

M
march3 已提交
211
    def turn(self):
三月三net's avatar
三月三net 已提交
212 213 214 215 216
        if hasattr(self.body_view.body, "torus_stars"):
            # 星环小天体群(主要模拟小行星群,非一个天体)不受 body_size_factor 影响
            self.scale = self.init_scale
        else:
            self.scale = self.init_scale * UrsinaConfig.body_size_factor
三月三net's avatar
三月三net 已提交
217

三月三net's avatar
三月三net 已提交
218
        pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
219 220 221 222 223 224
        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 已提交
225

三月三net's avatar
三月三net 已提交
226 227 228 229 230 231 232 233
        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 已提交
234
            # 是通过月球保持一面面对地球,调整得到
三月三net's avatar
三月三net 已提交
235 236
            self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
                            UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
三月三net's avatar
三月三net 已提交
237 238
            # rotation_speed 度/小时  dt 秒 = (dt / 3600)小时

三月三net's avatar
三月三net 已提交
239 240 241 242 243 244
        # if self.rotation_y < 0:
        #     self.rotation_y += 360
        try:
            # 天体旋转
            self.rotation_y -= self.rotspeed
        except Exception as e:
三月三net's avatar
三月三net 已提交
245
            print(self.body_view.body)
三月三net's avatar
三月三net 已提交
246
            self.destroy_all()
三月三net's avatar
三月三net 已提交
247 248 249 250 251 252 253 254
            return

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

三月三net's avatar
三月三net 已提交
256 257 258 259 260 261
        if UrsinaConfig.show_trail:
            # 有时候第一个位置不正确,所以判断一下有历史记录后在创建
            if len(self.body_view.body.his_position()) > 1:
                self.create_trails()
        else:
            self.clear_trails()
三月三net's avatar
三月三net 已提交
262

三月三net's avatar
三月三net 已提交
263
    def follow_parent(self):
三月三net's avatar
三月三net 已提交
264 265 266 267 268 269 270 271 272 273 274 275
        if not hasattr(self, "f_parent"):
            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:
                    self.f_parent = b
                    break
        pos = self.f_parent.position * UrsinaConfig.SCALE_FACTOR
        self.x = -pos[1]
        self.y = pos[2]
        self.z = pos[0]
三月三net's avatar
三月三net 已提交
276

M
march3 已提交
277
    def create_rings(self):
三月三net's avatar
三月三net 已提交
278
        """
三月三net's avatar
三月三net 已提交
279
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
280 281
        :return:
        """
三月三net's avatar
三月三net 已提交
282
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
283
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
284
        # 创建行星环
三月三net's avatar
三月三net 已提交
285 286 287
        # 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 已提交
288
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
289
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
290
        # 创建行星环
三月三net's avatar
三月三net 已提交
291
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
292
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
293 294 295
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

三月三net's avatar
三月三net 已提交
298 299 300 301 302 303
    def clear_trails(self):
        if not hasattr(self, "trails"):
            return
        # 删除拖尾
        for entity, pos in self.trails.items():
            destroy(entity)
三月三net's avatar
三月三net 已提交
304
        self.trails.clear()
三月三net's avatar
三月三net 已提交
305

三月三net's avatar
三月三net 已提交
306 307 308 309
    def destroy_all(self):
        # 从天体系统中移除自己(TODO:暂时还不能移除)
        # self.body_view.bodies_system.bodies.remove(self.body_view.body)
        # 删除拖尾
三月三net's avatar
三月三net 已提交
310
        self.clear_trails()
三月三net's avatar
三月三net 已提交
311
        # 如果有行星环,则删除行星环
三月三net's avatar
三月三net 已提交
312 313
        if hasattr(self, "ring"):
            destroy(self.ring)
三月三net's avatar
三月三net 已提交
314 315
        self.body_view.body.appeared = False
        self.body_view.appeared = False
三月三net's avatar
三月三net 已提交
316
        # 最后删除自己
三月三net's avatar
三月三net 已提交
317 318 319 320 321 322 323 324
        destroy(self)


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

三月三net's avatar
三月三net 已提交
325 326
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
327 328 329 330 331 332 333
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
334
    def update(self):
三月三net's avatar
三月三net 已提交
335
        """
三月三net's avatar
三月三net 已提交
336

三月三net's avatar
三月三net 已提交
337 338 339
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
340

三月三net's avatar
三月三net 已提交
341 342 343 344
    def appear(self):
        pass

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