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

M
march3 已提交
25

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

三月三net's avatar
三月三net 已提交
29
    """
三月三net's avatar
三月三net 已提交
30 31
    body_rotation_speed_control = 1.0

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

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

M
march3 已提交
64 65 66 67 68 69
    def input(self, key):
        if key == "escape":
            if mouse.locked:
                self.on_disable()
            else:
                sys.exit()
M
march3 已提交
70
        return super().input(key)
M
march3 已提交
71

三月三net's avatar
三月三net 已提交
72 73

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

M
march3 已提交
75 76
    def __init__(self, body_view: BodyView):
        self.body_view = body_view
三月三net's avatar
三月三net 已提交
77
        self.rotation_speed = self.body_view.body.rotation_speed
M
march3 已提交
78 79 80
        self.rotMode = 'x'  # random.choice(["x", "y", "z"])
        self.name = body_view.name

三月三net's avatar
三月三net 已提交
81 82
        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 已提交
83

三月三net's avatar
三月三net 已提交
84
        if hasattr(body_view, "texture"):
三月三net's avatar
三月三net 已提交
85 86 87
            texture = load_texture(body_view.texture)
        else:
            texture = None
M
march3 已提交
88

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

三月三net's avatar
三月三net 已提交
98
        super().__init__(
三月三net's avatar
三月三net 已提交
99
            # model="sphere",
三月三net's avatar
三月三net 已提交
100
            model=model,
三月三net's avatar
三月三net 已提交
101 102 103 104
            scale=scale,
            texture=texture,
            color=color.white,
            position=pos,
三月三net's avatar
三月三net 已提交
105
            rotation=rotation,
三月三net's avatar
三月三net 已提交
106
            double_sided=True)
三月三net's avatar
三月三net 已提交
107

三月三net's avatar
三月三net 已提交
108
        if hasattr(self.body_view.body, "torus_stars"):
三月三net's avatar
三月三net 已提交
109
            # 星环小天体群(主要模拟小行星群,非一个天体)
三月三net's avatar
三月三net 已提交
110
            self.set_light_off()
三月三net's avatar
三月三net 已提交
111 112
        else:
            # 一个天体
三月三net's avatar
三月三net 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
            # 拖尾球体的初始化
            self.trail_init()

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

        # 根据天体的颜色获取拖尾的颜色
        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 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147

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

三月三net's avatar
三月三net 已提交
170
        # 创建拖尾球体,并作为字典的key,存放拖尾球体的位置
三月三net's avatar
三月三net 已提交
171
        self.trails[self.create_trail(pos)] = pos
三月三net's avatar
三月三net 已提交
172 173 174 175 176 177

        # 计算拖尾球体超过的数量
        trail_overflow_count = len(self.trails) - self.trail_len

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

三月三net's avatar
三月三net 已提交
184
    def create_trail(self, pos):
三月三net's avatar
三月三net 已提交
185 186 187 188 189 190
        """
        在天体当前的位置创建一个拖尾球体
        :param pos:
        :return:
        """
        trail = Entity(model="sphere", color=self.trail_color, scale=self.trail_scale, position=pos)
三月三net's avatar
三月三net 已提交
191 192
        trail.set_light_off()
        return trail
三月三net's avatar
三月三net 已提交
193

M
march3 已提交
194
    def turn(self):
三月三net's avatar
三月三net 已提交
195
        pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
三月三net's avatar
三月三net 已提交
196

M
march3 已提交
197 198 199
        self.x = -pos[1]
        self.y = pos[2]
        self.z = pos[0]
M
march3 已提交
200

三月三net's avatar
三月三net 已提交
201 202 203 204 205 206 207 208
        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 已提交
209
            # 是通过月球保持一面面对地球,调整得到
三月三net's avatar
三月三net 已提交
210 211
            self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
                            UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
三月三net's avatar
三月三net 已提交
212 213
            # rotation_speed 度/小时  dt 秒 = (dt / 3600)小时

三月三net's avatar
三月三net 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
        # if self.rotation_y < 0:
        #     self.rotation_y += 360
        try:
            # 天体旋转
            self.rotation_y -= self.rotspeed
        except Exception as e:
            self.destroy_me()
            return

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

三月三net's avatar
三月三net 已提交
230 231 232
        # 有时候第一个位置不正确,所以判断一下有历史记录后在创建
        if len(self.body_view.body.his_position()) > 1:
            self.create_trails()
三月三net's avatar
三月三net 已提交
233

M
march3 已提交
234
    def create_rings(self):
三月三net's avatar
三月三net 已提交
235
        """
三月三net's avatar
三月三net 已提交
236
        创建行星环(使用土星贴图)
三月三net's avatar
三月三net 已提交
237 238
        :return:
        """
三月三net's avatar
三月三net 已提交
239
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
240
        # self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
241
        # 创建行星环
三月三net's avatar
三月三net 已提交
242 243 244
        # 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 已提交
245
        # 行星环偏移角度
三月三net's avatar
三月三net 已提交
246
        self.ring_rotation_x = 80
三月三net's avatar
三月三net 已提交
247
        # 创建行星环
三月三net's avatar
三月三net 已提交
248
        torus = create_torus(0.7, 1.2, 64)
三月三net's avatar
三月三net 已提交
249
        self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
三月三net's avatar
三月三net 已提交
250 251 252
                           rotation=(self.ring_rotation_x, 0, 0), double_sided=True)

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

三月三net's avatar
三月三net 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    def destroy_me(self):
        for entity, pos in self.trails.items():
            destroy(entity)
        if hasattr(self, "ring"):
            destroy(self.ring)
        destroy(self)


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

    def __init__(self, body: Body):
        BodyView.__init__(self, body)
        self.velocity = body.velocity

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

三月三net's avatar
三月三net 已提交
277
    def update(self):
三月三net's avatar
三月三net 已提交
278
        """
三月三net's avatar
三月三net 已提交
279

三月三net's avatar
三月三net 已提交
280 281 282
        :return:
        """
        self.planet.turn()
三月三net's avatar
三月三net 已提交
283

三月三net's avatar
三月三net 已提交
284 285 286 287 288

    def appear(self):
        pass

    def disappear(self):
三月三net's avatar
三月三net 已提交
289
        self.planet.destroy_me()