ursina_view.py 11.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
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 32
    body_rotation_speed_control = 1.0

M
march3 已提交
33
    def __init__(self, position, 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.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
54
        # 将摄像机的观察角度绕 x 轴旋转 45 度,绕 y 轴旋转 0 度,绕 z 轴旋转 0 度
三月三net's avatar
三月三net 已提交
55 56
        # camera.rotation = Vec3(45, 90, 0)
        camera.rotation = Vec3(0, 0, 0)
M
march3 已提交
57

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

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

三月三net's avatar
三月三net 已提交
74 75

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

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

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

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

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

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

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

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

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

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

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

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

三月三net's avatar
三月三net 已提交
192
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
193 194 195 196 197 198
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
        trail = Entity(model="sphere", color=self.trail_color, scale=self.trail_scale, position=pos)
三月三net's avatar
三月三net 已提交
199 200
        trail.set_light_off()
        return trail
三月三net's avatar
三月三net 已提交
201

M
march3 已提交
202
    def turn(self):
三月三net's avatar
三月三net 已提交
203 204 205

        self.scale = self.init_scale * UrsinaConfig.body_size_factor

三月三net's avatar
三月三net 已提交
206
        pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
207 208 209 210 211 212
        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 已提交
213

三月三net's avatar
三月三net 已提交
214 215 216 217 218 219 220 221
        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 已提交
222
            # 是通过月球保持一面面对地球,调整得到
三月三net's avatar
三月三net 已提交
223 224
            self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
                            UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
三月三net's avatar
三月三net 已提交
225 226
            # rotation_speed 度/小时  dt 秒 = (dt / 3600)小时

三月三net's avatar
三月三net 已提交
227 228 229 230 231 232
        # if self.rotation_y < 0:
        #     self.rotation_y += 360
        try:
            # 天体旋转
            self.rotation_y -= self.rotspeed
        except Exception as e:
三月三net's avatar
三月三net 已提交
233
            print(self.body_view.body)
三月三net's avatar
三月三net 已提交
234
            self.destroy_all()
三月三net's avatar
三月三net 已提交
235 236 237 238 239 240 241 242
            return

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

三月三net's avatar
三月三net 已提交
244 245 246 247 248 249
        if UrsinaConfig.show_trail:
            # 有时候第一个位置不正确,所以判断一下有历史记录后在创建
            if len(self.body_view.body.his_position()) > 1:
                self.create_trails()
        else:
            self.clear_trails()
三月三net's avatar
三月三net 已提交
250

三月三net's avatar
三月三net 已提交
251 252 253 254 255 256 257 258 259 260 261
    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 已提交
262
    def create_rings(self):
三月三net's avatar
三月三net 已提交
263
        """
三月三net's avatar
三月三net 已提交
264
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
265 266
        :return:
        """
三月三net's avatar
三月三net 已提交
267
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
268
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
269
        # 创建行星环
三月三net's avatar
三月三net 已提交
270 271 272
        # 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 已提交
273
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
274
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
275
        # 创建行星环
三月三net's avatar
三月三net 已提交
276
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
277
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
278 279 280
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

三月三net's avatar
三月三net 已提交
283 284 285 286 287 288
    def clear_trails(self):
        if not hasattr(self, "trails"):
            return
        # 删除拖尾
        for entity, pos in self.trails.items():
            destroy(entity)
三月三net's avatar
三月三net 已提交
289
        self.trails.clear()
三月三net's avatar
三月三net 已提交
290

三月三net's avatar
三月三net 已提交
291 292 293 294
    def destroy_all(self):
        # 从天体系统中移除自己(TODO:暂时还不能移除)
        # self.body_view.bodies_system.bodies.remove(self.body_view.body)
        # 删除拖尾
三月三net's avatar
三月三net 已提交
295
        self.clear_trails()
三月三net's avatar
三月三net 已提交
296
        # 如果有行星环,则删除行星环
三月三net's avatar
三月三net 已提交
297 298
        if hasattr(self, "ring"):
            destroy(self.ring)
三月三net's avatar
三月三net 已提交
299 300
        self.body_view.body.appeared = False
        self.body_view.appeared = False
三月三net's avatar
三月三net 已提交
301
        # 最后删除自己
三月三net's avatar
三月三net 已提交
302 303 304 305 306 307 308 309
        destroy(self)


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

三月三net's avatar
三月三net 已提交
310 311
    def __init__(self, body: Body, bodies_system):
        BodyView.__init__(self, body, bodies_system)
三月三net's avatar
三月三net 已提交
312 313 314 315 316 317 318
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
319
    def update(self):
三月三net's avatar
三月三net 已提交
320
        """
三月三net's avatar
三月三net 已提交
321

三月三net's avatar
三月三net 已提交
322 323 324
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
325

三月三net's avatar
三月三net 已提交
326 327 328 329
    def appear(self):
        pass

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