ursina_view.py 13.3 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, PointLight
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 122
        if hasattr(self.body_view.body, "torus_stars") or \
                hasattr(self.body_view.body, "light_disable"):
三月三net's avatar
三月三net 已提交
123
            # 星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
124
            # 或者灯光禁用
三月三net's avatar
三月三net 已提交
125
            self.set_light_off()
三月三net's avatar
三月三net 已提交
126
            self.double_sided = True
三月三net's avatar
三月三net 已提交
127 128
        else:
            # 一个天体
三月三net's avatar
三月三net 已提交
129 130
            # 拖尾球体的初始化
            self.trail_init()
三月三net's avatar
三月三net 已提交
131 132
            if self.body_view.body.is_fixed_star:
                self.create_fixed_star_lights()
三月三net's avatar
三月三net 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

    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 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163

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

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

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

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

三月三net's avatar
三月三net 已提交
205
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
206 207 208 209 210
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
三月三net's avatar
三月三net 已提交
211 212
        # 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 已提交
213
        trail.set_light_off()
三月三net's avatar
三月三net 已提交
214 215 216
        # trail.set_color_off()
        # trail.set_color_scale_off()
        # trail.enabled = False
三月三net's avatar
三月三net 已提交
217
        return trail
三月三net's avatar
三月三net 已提交
218

M
march3 已提交
219
    def turn(self):
三月三net's avatar
三月三net 已提交
220 221 222 223 224
        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 已提交
225

三月三net's avatar
三月三net 已提交
226
        pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
227 228 229 230 231 232
        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 已提交
233

三月三net's avatar
三月三net 已提交
234 235 236 237 238 239 240 241
        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 已提交
242
            # 是通过月球保持一面面对地球,调整得到
三月三net's avatar
三月三net 已提交
243 244
            self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
                            UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
三月三net's avatar
三月三net 已提交
245 246
            # rotation_speed 度/小时  dt 秒 = (dt / 3600)小时

三月三net's avatar
三月三net 已提交
247 248 249 250 251 252
        # if self.rotation_y < 0:
        #     self.rotation_y += 360
        try:
            # 天体旋转
            self.rotation_y -= self.rotspeed
        except Exception as e:
三月三net's avatar
三月三net 已提交
253
            print(self.body_view.body)
三月三net's avatar
三月三net 已提交
254
            self.destroy_all()
三月三net's avatar
三月三net 已提交
255 256 257 258 259 260 261 262
            return

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

三月三net's avatar
三月三net 已提交
264 265 266 267 268 269
        if UrsinaConfig.show_trail:
            # 有时候第一个位置不正确,所以判断一下有历史记录后在创建
            if len(self.body_view.body.his_position()) > 1:
                self.create_trails()
        else:
            self.clear_trails()
三月三net's avatar
三月三net 已提交
270

三月三net's avatar
三月三net 已提交
271
    def follow_parent(self):
三月三net's avatar
三月三net 已提交
272 273 274 275 276 277 278 279 280 281 282 283
        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 已提交
284

三月三net's avatar
三月三net 已提交
285 286 287 288 289 290 291 292 293 294 295 296
    def create_fixed_star_lights(self):
        """
        创建恒星的发光的效果、并作为灯光源
        :param entity:
        :return:
        """

        # 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
        self.set_light_off()

        # lights = []
        # # 创建多个新的 Entity 对象,作为光晕的容器
三月三net's avatar
三月三net 已提交
297 298 299
        # _color = color.rgba(1.0, 0.6, 0.2, 1)
        _color = self.body_view.body.color
        _color = color.rgba(_color[0]/255, _color[1]/255, _color[2]/255, 1)
三月三net's avatar
三月三net 已提交
300
        for i in range(10):
三月三net's avatar
三月三net 已提交
301
            glow_entity = Entity(parent=self, model='sphere', color=_color,
三月三net's avatar
三月三net 已提交
302 303 304 305 306
                                 scale=math.pow(1.03, i), alpha=0.1)
        for i in range(2):
            # 创建 PointLight 对象,作为恒星的灯光源
            light = PointLight(parent=self, intensity=10, range=10, color=color.white)

M
march3 已提交
307
    def create_rings(self):
三月三net's avatar
三月三net 已提交
308
        """
三月三net's avatar
三月三net 已提交
309
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
310 311
        :return:
        """
三月三net's avatar
三月三net 已提交
312
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
313
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
314
        # 创建行星环
三月三net's avatar
三月三net 已提交
315 316 317
        # 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 已提交
318
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
319
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
320
        # 创建行星环
三月三net's avatar
三月三net 已提交
321
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
322
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
323 324 325
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

三月三net's avatar
三月三net 已提交
328 329 330 331 332 333
    def clear_trails(self):
        if not hasattr(self, "trails"):
            return
        # 删除拖尾
        for entity, pos in self.trails.items():
            destroy(entity)
三月三net's avatar
三月三net 已提交
334
        self.trails.clear()
三月三net's avatar
三月三net 已提交
335

三月三net's avatar
三月三net 已提交
336 337 338 339
    def destroy_all(self):
        # 从天体系统中移除自己(TODO:暂时还不能移除)
        # self.body_view.bodies_system.bodies.remove(self.body_view.body)
        # 删除拖尾
三月三net's avatar
三月三net 已提交
340
        self.clear_trails()
三月三net's avatar
三月三net 已提交
341
        # 如果有行星环,则删除行星环
三月三net's avatar
三月三net 已提交
342 343
        if hasattr(self, "ring"):
            destroy(self.ring)
三月三net's avatar
三月三net 已提交
344 345
        self.body_view.body.appeared = False
        self.body_view.appeared = False
三月三net's avatar
三月三net 已提交
346
        # 最后删除自己
三月三net's avatar
三月三net 已提交
347 348 349 350 351 352 353 354
        destroy(self)


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

三月三net's avatar
三月三net 已提交
355 356
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
357 358 359 360 361 362 363
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
364
    def update(self):
三月三net's avatar
三月三net 已提交
365
        """
三月三net's avatar
三月三net 已提交
366

三月三net's avatar
三月三net 已提交
367 368 369
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
370

三月三net's avatar
三月三net 已提交
371 372 373 374
    def appear(self):
        pass

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