ursina_view.py 14.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, 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
        UrsinaEvent.on_reset_subscription(self.on_reset)
三月三net's avatar
三月三net 已提交
110 111 112
        # color.white
        self.plant_color = color.white
        # self.plant_color = color.rgba(*self.body_view.color)
三月三net's avatar
三月三net 已提交
113
        super().__init__(
三月三net's avatar
三月三net 已提交
114
            # model="sphere",
三月三net's avatar
三月三net 已提交
115
            model=model,
三月三net's avatar
三月三net 已提交
116 117
            scale=scale,
            texture=texture,
三月三net's avatar
三月三net 已提交
118
            color=self.plant_color,
三月三net's avatar
三月三net 已提交
119
            position=pos,
三月三net's avatar
三月三net 已提交
120 121
            rotation=rotation,
            double_sided=True
三月三net's avatar
三月三net 已提交
122
        )
三月三net's avatar
三月三net 已提交
123

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

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

        # 根据天体的颜色获取拖尾的颜色
三月三net's avatar
三月三net 已提交
146
        trail_color = conv_to_vec4_color(self.body_view.body.trail_color)
三月三net's avatar
三月三net 已提交
147 148 149 150 151 152 153
        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 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # lights = []
        # # 创建多个新的 Entity 对象,作为光晕的容器
三月三net's avatar
三月三net 已提交
300
        # _color = color.rgba(1.0, 0.6, 0.2, 1)
三月三net's avatar
三月三net 已提交
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
        if hasattr(self.body_view.body, "glows"):
            # glows = (glow_num:10, glow_scale:1.03 glow_alpha:0.1~1)
            glows = self.body_view.body.glows
            if glows is not None:
                if isinstance(glows, tuple):
                    if len(glows) == 3:
                        glow_num, glow_scale, glow_alpha = glows
                    elif len(glows) == 2:
                        glow_num, glow_scale = glows
                        glow_alpha = None
                else:
                    glow_num = glows
                    glow_scale = 1.02
                    glow_alpha = None

                if glow_num > 0:
                    glow_alphas = [0, 0.5, 0.4, 0.3, 0.2, 0.1]
                    if glow_alpha is None:
                        if glow_num < len(glow_alphas)-1:
                            glow_alpha = glow_alphas[glow_num]
                        else:
                            glow_alpha = glow_alphas[-1]

                    # _color = color.white
                    _color = self.body_view.body.color
                    _color = color.rgba(_color[0]/255, _color[1]/255, _color[2]/255, 1)
                    for i in range(glow_num):
                        glow_entity = Entity(parent=self, model='sphere', color=_color,
                                             scale=math.pow(glow_scale, i+1), alpha=glow_alpha)
三月三net's avatar
三月三net 已提交
330 331 332 333 334
        if hasattr(self.body_view.body, "light_on"):
            if self.body_view.body.light_on:
                for i in range(2):
                    # 创建 PointLight 对象,作为恒星的灯光源
                    light = PointLight(parent=self, intensity=10, range=10, color=color.white)
三月三net's avatar
三月三net 已提交
335

M
march3 已提交
336
    def create_rings(self):
三月三net's avatar
三月三net 已提交
337
        """
三月三net's avatar
三月三net 已提交
338
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
339 340
        :return:
        """
三月三net's avatar
三月三net 已提交
341
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
342
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
343
        # 创建行星环
三月三net's avatar
三月三net 已提交
344 345 346
        # 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 已提交
347
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
348
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
349
        # 创建行星环
三月三net's avatar
三月三net 已提交
350
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
351
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
352 353 354
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

三月三net's avatar
三月三net 已提交
357 358 359 360 361 362
    def clear_trails(self):
        if not hasattr(self, "trails"):
            return
        # 删除拖尾
        for entity, pos in self.trails.items():
            destroy(entity)
三月三net's avatar
三月三net 已提交
363
        self.trails.clear()
三月三net's avatar
三月三net 已提交
364

三月三net's avatar
三月三net 已提交
365 366 367 368
    def destroy_all(self):
        # 从天体系统中移除自己(TODO:暂时还不能移除)
        # self.body_view.bodies_system.bodies.remove(self.body_view.body)
        # 删除拖尾
三月三net's avatar
三月三net 已提交
369
        self.clear_trails()
三月三net's avatar
三月三net 已提交
370
        # 如果有行星环,则删除行星环
三月三net's avatar
三月三net 已提交
371 372
        if hasattr(self, "ring"):
            destroy(self.ring)
三月三net's avatar
三月三net 已提交
373 374
        self.body_view.body.appeared = False
        self.body_view.appeared = False
三月三net's avatar
三月三net 已提交
375
        # 最后删除自己
三月三net's avatar
三月三net 已提交
376 377 378 379 380 381 382 383
        destroy(self)


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

三月三net's avatar
三月三net 已提交
384 385
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
386 387 388 389 390 391 392
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
393
    def update(self):
三月三net's avatar
三月三net 已提交
394
        """
三月三net's avatar
三月三net 已提交
395

三月三net's avatar
三月三net 已提交
396 397 398
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
399

三月三net's avatar
三月三net 已提交
400 401 402 403
    def appear(self):
        pass

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