ursina_view.py 11.4 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
M
march3 已提交
10 11 12 13
from ursina import Ursina, window, Entity, Mesh, SmoothFollow, Texture, clamp, time, camera, color, mouse, Vec2, Vec3, \
    load_texture, held_keys

# from ursina.camera import OrthographicCamera
M
march3 已提交
14 15 16 17 18
from math import sin, cos, radians
from ursina.prefabs.first_person_controller import FirstPersonController
import sys
import random as rd
import os
三月三net's avatar
三月三net 已提交
19 20 21 22
from bodies import Body
import random
from simulators.views.body_view import BodyView
import numpy as np
M
march3 已提交
23
import math
三月三net's avatar
三月三net 已提交
24 25
from math import sin, cos, radians
import os
M
march3 已提交
26
import matplotlib.pyplot as plt
三月三net's avatar
三月三net 已提交
27

三月三net's avatar
三月三net 已提交
28
SCALE_FACTOR = 1e-6
M
march3 已提交
29 30


M
march3 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
# 创建 TorusMesh
class TorusMesh(Mesh):
    def __init__(self, radius=1, thickness=.25, radial_segments=16, tubular_segments=32):
        super().__init__()
        self.radius = radius
        self.thickness = thickness
        self.radial_segments = radial_segments
        self.tubular_segments = tubular_segments
        self.vertices = []
        self.triangles = []

        for j in range(radial_segments):
            for i in range(tubular_segments):
                a = i / tubular_segments * math.pi * 2
                b = j / radial_segments * math.pi * 2
                x = (radius + thickness * math.cos(b)) * math.cos(a)
                y = (radius + thickness * math.cos(b)) * math.sin(a)
                z = thickness * math.sin(b)
                u = i / tubular_segments
                v = j / radial_segments
                self.vertices.append((x, y, z))
                self.uvs.append((u, v))

        for j in range(radial_segments):
            for i in range(tubular_segments):
                a = i
                b = j
                c = (i + 1) % tubular_segments
                d = (j + 1) % radial_segments
                self.triangles.append((a + b * tubular_segments, b + d * tubular_segments, c + b * tubular_segments))
                self.triangles.append((c + b * tubular_segments, b + d * tubular_segments, c + d * tubular_segments))


M
march3 已提交
64
class UrsinaPlayer(FirstPersonController):
M
march3 已提交
65
    def __init__(self, position, targets=None):
M
march3 已提交
66 67 68 69
        # global planets
        super().__init__()
        # pos = planets[0].position
        camera.fov = 100
M
march3 已提交
70 71
        camera.rotation_y = 90
        self.planets = None
M
march3 已提交
72
        if targets is not None:
M
march3 已提交
73
            self.planets = []
M
march3 已提交
74
            # targets = [view.planet.parent for view in targets]
M
march3 已提交
75
            # targets_parent = Entity()
M
march3 已提交
76
            for view in targets:
M
march3 已提交
77 78 79 80 81 82
                # 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)))
M
march3 已提交
83 84
        pos = np.array(position) * SCALE_FACTOR
        self.position = Vec3(pos[0], pos[1], pos[2])
M
march3 已提交
85 86 87 88 89 90
        # 将摄像机位置设置为 x=0、y=1、z=0 的位置
        # camera.position = Vec3(pos[0], pos[1], pos[2])
        self.position = Vec3(pos[0], pos[1], pos[2])
        # 将摄像机的观察角度绕 x 轴旋转 45 度,绕 y 轴旋转 0 度,绕 z 轴旋转 0 度
        camera.rotation = Vec3(45, 90, 0)

M
march3 已提交
91
        self.gravity = 0
M
march3 已提交
92 93
        self.vspeed = 400
        self.speed = 1000
M
march3 已提交
94 95
        self.mouse_sensitivity = Vec2(160, 160)
        self.on_enable()
M
march3 已提交
96 97
        self.rotation_speed = 80

M
march3 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    # def space_callback(self):
    #     best_camera_position = self.best_camera_position()
    #     if best_camera_position is None:
    #         return
    #     best_pos = np.array(best_camera_position[0]) * SCALE_FACTOR
    #     camera.position, camera.rotation = best_pos, best_camera_position[1]
    #
    # def best_camera_position(self):
    #     if self.planets is None:
    #         return None
    #
    #     min_x, max_x, min_y, max_y, min_z, max_z = None, None, None, None, None, None
    #     for planet in self.planets:
    #         if min_x is None or planet.x < min_x:
    #             min_x = planet.x
    #         if max_x is None or planet.x > max_x:
    #             max_x = planet.x
    #         if min_y is None or planet.y < min_y:
    #             min_y = planet.y
    #         if max_y is None or planet.y > max_y:
    #             max_y = planet.y
    #         if min_z is None or planet.z < min_z:
    #             min_z = planet.z
    #         if max_z is None or planet.z > max_z:
    #             max_z = planet.z
    #     x = (min_x + max_x) / 2
    #     y = (min_y + max_y) / 2
    #     z = (min_z + max_z) / 2
    #     distance = max(max_x - min_x, max_y - min_y, max_z - min_z) * 1.5
    #     return (x, y + distance, z - distance), (0, -90, 0)
M
march3 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140 141

    # def camera_adj(self, targets):
    #     # 创建一些物体
    #     # targets = []
    #     # for i in range(10):
    #     #     targets.append(Entity(model='cube', color=color.random_color(), position=(i * 2 - 10, i % 2 * 2 - 1, 0)))
    #
    #     # 计算所有物体的平均位置
    #     avg_pos = sum(np.array([target.position for target in targets]))/ len(targets)
    #
    #     # 创建一个OrthographicCamera,使其位于所有目标的中心位置,并面向下方
    #     # camera = OrthographicCamera()
    #     camera.position = avg_pos + (0, 0, 20)
    #     camera.rotation_x = -90
M
march3 已提交
142 143 144 145 146 147 148

    def input(self, key):
        if key == "escape":
            if mouse.locked:
                self.on_disable()
            else:
                sys.exit()
M
march3 已提交
149
        return super().input(key)
M
march3 已提交
150 151 152
        # elif key == "space":
        #     self.space_callback()
        # Input.bind('space', space_callback)
M
march3 已提交
153

M
march3 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    # def _update(self):
    #     # # {'left mouse': 0, 'left': 0, 'left shift': 0, 'space': 1, 'w': 0, 's': 0, 'd': 0, 'a': 0, 'shift': 0})
    #     # if held_keys["left mouse"]:
    #     #     self.on_enable()
    #     # if held_keys["left shift"]:
    #     #     self.y -= self.vspeed
    #     # if held_keys["space"]:
    #     #     self.y += self.vspeed
    #     # WASD keys
    #
    #     if held_keys['f']:
    #         camera.fov += 1
    #     if held_keys['r']:
    #         camera.fov -= 1
    #     # camera.position
    #     # self.rotation_y += held_keys['d'] * self.rotation_speed * time.dt
    #     # self.rotation_y -= held_keys['a'] * self.rotation_speed * time.dt
    #     # self.rotation_x += held_keys['w'] * self.rotation_speed * time.dt
    #     # self.rotation_x -= held_keys['s'] * self.rotation_speed * time.dt
    #
    #     forward = self.forward * (held_keys['w'] - held_keys['s'])
    #     right = self.right * (held_keys['d'] - held_keys['a'])
    #
    #     self.position += (forward + right) * self.speed * time.dt
    #     # if held_keys['w']:
    #     #     camera.position += camera.forward * time.dt
    #     # if held_keys['s']:
    #     #     camera.position -= camera.forward * time.dt
    #     # if held_keys['a']:
    #     #     camera.position -= camera.right * time.dt
    #     # if held_keys['d']:
    #     #     camera.position += camera.right * time.dt
    #     #
    #     # # Mouse control
    #     # camera.rotation_y += held_keys['right mouse'] * mouse.velocity[0] * 20
    #     # camera.rotation_x -= held_keys['right mouse'] * mouse.velocity[1] * 20
    #     # camera.rotation_x = clamp(camera.rotation_x, -90, 90)
M
march3 已提交
191

三月三net's avatar
三月三net 已提交
192 193

class Planet(Entity):
M
march3 已提交
194 195
    def __init__(self, body_view: BodyView):
        self.body_view = body_view
M
march3 已提交
196 197 198
        # self.angle = 0  # random.uniform(0.0005, 0.01)
        # self.fastMode = 0
        # self.rotation = (random.randint(0, 360) for i in range(3))
三月三net's avatar
三月三net 已提交
199 200
        # 旋转速度和大小成反比(未使用真实数据)
        self.rotspeed = 30000 / self.body_view.raduis  # random.uniform(1.0, 2.0)
M
march3 已提交
201 202 203
        self.rotMode = 'x'  # random.choice(["x", "y", "z"])
        self.name = body_view.name

M
march3 已提交
204
        pos = body_view.position * body_view.body.distance_scale * SCALE_FACTOR
M
march3 已提交
205 206
        scale = body_view.body.diameter * body_view.body.size_scale * SCALE_FACTOR

三月三net's avatar
三月三net 已提交
207 208
        # texture = eval(f"{_type}_texture")
        # e = os.path.exists(texture)
M
march3 已提交
209
        # texture = self.__set_texture(body_view.texture)
三月三net's avatar
三月三net 已提交
210
        if hasattr(body_view, "texture"):
三月三net's avatar
三月三net 已提交
211 212 213
            texture = load_texture(body_view.texture)
        else:
            texture = None
M
march3 已提交
214

三月三net's avatar
三月三net 已提交
215 216 217 218 219 220 221 222 223 224
        # 将贴图旋转90度C:\ProgramData\Anaconda3\envs\tf_gpu\Lib\site-packages\ursina\models_compressed\diamond.ursinamesh
        super().__init__(
            # model=TorusMesh(radius=1e20, thickness=1.25e20, radial_segments=16, tubular_segments=32),
            # model="sky_dome",
            model="sphere",
            scale=scale,
            texture=texture,
            color=color.white,
            position=pos,
            rotation=(0, 0, 0))
三月三net's avatar
三月三net 已提交
225

M
march3 已提交
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    # def __set_texture(self, image_file):
    #     """
    #     设置纹理图片到天体
    #     :param image_file:
    #     :return:
    #     """
    #     outfile = image_file.replace('.png', '_rotate.png').replace('.jpg', '_rotate.jpg')
    #     from PIL import Image
    #     # 打开原始图片
    #     image = Image.open(image_file)
    #     # 顺时针旋转90度
    #     rotated_image = image.rotate(90, expand=True)
    #
    #     # 保存旋转后的图片
    #     rotated_image.save(outfile)
    #
    #     return outfile

    def turn(self):
M
march3 已提交
245 246 247 248 249
        # if self.name != "sun":
        #     if self.fastMode:
        #         angle *= 200
        # self.x = self.x * cos(radians(angle)) - self.y * sin(radians(angle))
        # self.y = self.x * sin(radians(angle)) + self.y * cos(radians(angle))
M
march3 已提交
250
        pos = self.body_view.position * SCALE_FACTOR
三月三net's avatar
三月三net 已提交
251

M
march3 已提交
252 253 254
        self.x = -pos[1]
        self.y = pos[2]
        self.z = pos[0]
M
march3 已提交
255

M
march3 已提交
256
        self.rotation_y -= self.rotspeed
M
march3 已提交
257

三月三net's avatar
三月三net 已提交
258 259 260 261 262 263 264 265 266 267 268 269
    def input(self, key):
        if key == "enter":
            self.fastMode = 1 - self.fastMode


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

    def __init__(self, body: Body):
        BodyView.__init__(self, body)
M
march3 已提交
270 271 272 273 274 275 276 277 278
        self.velocity = body.velocity
        # 将列表元素往后移动一位,最后一位变为第一位
        # a1 = a[1:] + [a[0]]
        # self.velocity = np.concatenate((body.velocity[1:], [body.velocity[0]]))

        # 将列表元素往前移动一位,第一位变为最后一位
        # a2 = [a[-1]] + a[:-1]
        # self.velocity = np.concatenate((np.array([body.velocity[-1]]), body.velocity[0:2]))

M
march3 已提交
279
        self.planet = Planet(self)
M
march3 已提交
280 281 282
        if body.has_rings:
            self.create_rings()

三月三net's avatar
三月三net 已提交
283 284 285 286 287
        # self.planet.clear_light()
        if self.body.is_fixed_star:
            # 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
            self.planet.set_light_off()

M
march3 已提交
288 289 290 291 292 293
    def create_rings(self):
        ring = Entity(model='torus', texture='textures/saturnRings.jpg', scale=(4, 4), double_sided=True)

        # ring = Entity(model=TorusMesh(radius=self.body.diameter * 100, thickness=.5), texture='textures/saturnRings.jpg', scale=3)
        ring.world_parent = self.planet
        ring.position = self.planet.position
三月三net's avatar
三月三net 已提交
294 295

    def update(self):
M
march3 已提交
296
        self.planet.turn()
三月三net's avatar
三月三net 已提交
297 298
        if hasattr(self, "light"):
            self.light.position = Vec3(self.planet.x, self.planet.y, self.planet.z)
三月三net's avatar
三月三net 已提交
299 300 301 302 303

    def appear(self):
        pass

    def disappear(self):
M
march3 已提交
304
        self.planet.disable()