ursina_view.py 11.9 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
    # body_rotation_speed_control = 1.0
三月三net's avatar
三月三net 已提交
32

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

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

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

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

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

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

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

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

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

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

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

三月三net's avatar
三月三net 已提交
120
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
121
            # 星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
122
            self.set_light_off()
三月三net's avatar
三月三net 已提交
123
            self.double_sided = True
三月三net's avatar
三月三net 已提交
124 125
        else:
            # 一个天体
三月三net's avatar
三月三net 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
            # 拖尾球体的初始化
            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 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158

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

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

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

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

三月三net's avatar
三月三net 已提交
196
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
197 198 199 200 201
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
三月三net's avatar
三月三net 已提交
202 203
        # 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 已提交
204
        trail.set_light_off()
三月三net's avatar
三月三net 已提交
205 206 207
        # trail.set_color_off()
        # trail.set_color_scale_off()
        # trail.enabled = False
三月三net's avatar
三月三net 已提交
208
        return trail
三月三net's avatar
三月三net 已提交
209

M
march3 已提交
210
    def turn(self):
三月三net's avatar
三月三net 已提交
211 212 213 214 215
        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 已提交
216

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

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

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

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

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

三月三net's avatar
三月三net 已提交
262 263 264 265 266 267 268 269 270 271 272
    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 已提交
273
    def create_rings(self):
三月三net's avatar
三月三net 已提交
274
        """
三月三net's avatar
三月三net 已提交
275
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
276 277
        :return:
        """
三月三net's avatar
三月三net 已提交
278
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
279
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
280
        # 创建行星环
三月三net's avatar
三月三net 已提交
281 282 283
        # 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 已提交
284
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
285
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
286
        # 创建行星环
三月三net's avatar
三月三net 已提交
287
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
288
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
289 290 291
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

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

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


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

三月三net's avatar
三月三net 已提交
321 322
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
323 324 325 326 327 328 329
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
330
    def update(self):
三月三net's avatar
三月三net 已提交
331
        """
三月三net's avatar
三月三net 已提交
332

三月三net's avatar
三月三net 已提交
333 334 335
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
336

三月三net's avatar
三月三net 已提交
337 338 339 340
    def appear(self):
        pass

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