提交 4e5febd0 编写于 作者: 三月三net's avatar 三月三net

Python超人-宇宙模拟器

上级 40f623b2
...@@ -170,7 +170,21 @@ def rgb_to_hex(rgb): ...@@ -170,7 +170,21 @@ def rgb_to_hex(rgb):
def find_texture_root_path(): def find_texture_root_path():
paths = [os.path.join('.', 'textures'), os.path.join('..', 'textures'), os.path.join('..', '..', 'textures')] """
查找纹理图片的目录
@return:
"""
paths = [os.path.join('.', 'textures')]
# paths = [os.path.join('.', 'textures'), os.path.join('..', 'textures'),
# os.path.join('..', '..', 'textures'), os.path.join('..', '..', '..', 'textures'),
# os.path.join('..', '..', '..', '..', 'textures')]
for i in range(1, 5):
p = []
for j in range(i):
p.append("..")
p.append('textures')
paths.append(os.path.join(*p))
for path in paths: for path in paths:
if os.path.exists(path): if os.path.exists(path):
return path return path
......
...@@ -57,8 +57,8 @@ def mayavi_run(bodies, dt=SECONDS_PER_WEEK, ...@@ -57,8 +57,8 @@ def mayavi_run(bodies, dt=SECONDS_PER_WEEK,
def ursina_run(bodies, def ursina_run(bodies,
dt=SECONDS_PER_HALF_DAY, dt=SECONDS_PER_HALF_DAY,
position=(0, 0, 0), position=(0, 0, 0),
# view_azimuth=0, # view_azimuth=0, 摄像头观测方位角,可选,float类型(以度为单位,0-360)
light=True, # ignore_mass: 忽略所有天体的引力
cosmic_bg=None, cosmic_bg=None,
bg_music=None, bg_music=None,
show_grid=True, show_grid=True,
...@@ -67,22 +67,21 @@ def ursina_run(bodies, ...@@ -67,22 +67,21 @@ def ursina_run(bodies,
save_as_json=None, save_as_json=None,
view_closely=False): view_closely=False):
""" """
ursina 模拟器运行天体
:param bodies: 天体 @param bodies: 天体集合
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。 @param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param position: 摄像头位置 @param position: 摄像头位置
:param view_azimuth: 摄像头观测方位角,可选,float类型(以度为单位,0-360) @param cosmic_bg: 宇宙背景图片
:param light: 使用灯光效果 @param bg_music: 背景音乐
:param cosmic_bg: 宇宙背景图片 @param show_grid: 是否显示空间网格
:param show_grid: 是否显示空间网格 @param show_trail: 是否显示拖尾
:param save_as_json: 将所有天体的信息保存为 json 文件 @param show_name: 是否显示天体名称
:param ignore_mass: 忽略所有天体的引力 @param save_as_json: 将所有天体的信息保存为 json 文件
:return: @param view_closely: 是否近距离查看天体
@return:
""" """
from simulators.ursina_simulator import UrsinaSimulator, UrsinaPlayer from simulators.ursina_simulator import UrsinaSimulator, UrsinaPlayer
from ursina import application, Sequence, camera, held_keys, time, clamp, Entity, Text, color
from ursina.prefabs.first_person_controller import FirstPersonController
body_sys = System(bodies) body_sys = System(bodies)
if show_name: if show_name:
...@@ -101,37 +100,6 @@ def ursina_run(bodies, ...@@ -101,37 +100,6 @@ def ursina_run(bodies,
view_azimuth = 0 # 暂时未用 view_azimuth = 0 # 暂时未用
player = UrsinaPlayer(position, view_azimuth, simulator.ursina_views) player = UrsinaPlayer(position, view_azimuth, simulator.ursina_views)
# # player = FirstPersonController(model='cube', y=-1e20, color=color.orange, origin_y=-5000, speed=8)
# # player.on_disable()
# # player.position = position
#
# player = FirstPersonController()
# cube = Entity(model='cube', color=color.red, scale=2)
# player.parent = cube # 设置 FirstPersonController 的父实体为 cube
# cube.position = position # 修改父实体的位置,从而间接地修改 FirstPersonController 的位置
# # 创建一个实体(在屏幕中央)和一个摄像机
# TODO: 未使用
# entity = Entity(model='cube', position=(0, 0, 5), scale=2)
# camera = Camera()
#
# # 设置初始的 FOV 值(默认值为 90)
# camera.fov = 60
#
# # 创建一个用于显示当前 FOV 值的文本
# fov_text = Text(text=f'FOV: {camera.fov}', position=(-0.5, 0.4), scale=2)
# # 每一帧更新摄像机 FOV 值
# def update():
# # 通过鼠标滚轮来调整 FOV 值
# camera.fov -= held_keys['scroll'] * 10 * time.dt
# # 限制 FOV 值的范围(1 到 120 之间)
# camera.fov = clamp(camera.fov, 1, 120)
# # 更新文本内容
# fov_text.text = f'FOV: {camera.fov:.2f}' # 保留两位小数
#
# # 将摄像机移到实体旁边,并对着它
# camera.position = entity.position + (0, 0, -5)
# camera.look_at(entity.position)
def callback_update(): def callback_update():
UrsinaEvent.on_application_run() UrsinaEvent.on_application_run()
...@@ -146,7 +114,6 @@ def ursina_run(bodies, ...@@ -146,7 +114,6 @@ def ursina_run(bodies,
if show_trail: if show_trail:
UrsinaConfig.show_trail = show_trail UrsinaConfig.show_trail = show_trail
simulator.run(dt, simulator.run(dt,
light=light,
cosmic_bg=cosmic_bg, cosmic_bg=cosmic_bg,
show_grid=show_grid, show_grid=show_grid,
bg_music=bg_music, bg_music=bg_music,
......
...@@ -20,14 +20,14 @@ def show_text_bodies(): ...@@ -20,14 +20,14 @@ def show_text_bodies():
""" """
D = 6000 D = 6000
# camera_pos = 左-右+、上+下-、前+后- # camera_pos = 左-右+、上+下-、前+后-
camera_pos = (-130 * D, 0, -6000 * D) camera_pos = (D, D, -7000 * D)
bodies: list = gen_bodies_from_image(pixel_image="./images/python.png", texture="color_body.jpg", bodies: list = gen_bodies_from_image(pixel_image="./images/python.png", texture="color_body.png",
params={"camera_pos": camera_pos}) params={"camera_pos": camera_pos})
bg = FixedStar(name="bg", texture="fixed_star.png", mass=5e31, color=(0xff, 0xf8, 0xd4), # 放一个恒星作为背景
init_position=[3000 * D, 260 * D, 100 * D], # [ 远+近- , 左+右- , 上+下-] bg = FixedStar(name="bg", texture="fixed_star.png", mass=2e32, color=(0xff, 0xf8, 0xd4),
init_position=[6000 * D, 450 * D, 100 * D], # [ 远+近- , 左+右- , 上+下-]
ignore_mass=True) ignore_mass=True)
bg.light_on = True
bodies.append(bg) bodies.append(bg)
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
......
...@@ -19,14 +19,14 @@ def show_eye_of_god(): ...@@ -19,14 +19,14 @@ def show_eye_of_god():
""" """
D = 6000 D = 6000
# camera_pos = 左-右+、上+下-、前+后- # camera_pos = 左-右+、上+下-、前+后-
camera_pos = (-100 * D, 0, -6000 * D) camera_pos = (D, D, -7000 * D)
bodies: list = gen_bodies_from_image(pixel_image="./images/eye.png", bodies: list = gen_bodies_from_image(pixel_image="./images/eye.png", texture="color_body.jpg",
params={"camera_pos": camera_pos}) params={"camera_pos": camera_pos})
bg = FixedStar(name="bg", texture="fixed_star.png", mass=5e31, color=(0xff, 0xf8, 0xd4), # 放一个恒星作为背景
init_position=[3000 * D, 200 * D, 100 * D], # [ 远+近- , 左+右- , 上+下-] bg = FixedStar(name="bg", texture="fixed_star.png", mass=2e32, color=(0xff, 0xf8, 0xd4),
init_position=[6000 * D, 400 * D, 100 * D], # [ 远+近- , 左+右- , 上+下-]
ignore_mass=True) ignore_mass=True)
# bg.light_on = False
bodies.append(bg) bodies.append(bg)
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
......
...@@ -8,10 +8,27 @@ ...@@ -8,10 +8,27 @@
# ============================================================================== # ==============================================================================
from bodies import ColorBody from bodies import ColorBody
import random import random
import math
from PIL import Image from PIL import Image
def get_scaled_body_pos(camera_pos, body_pos, scale_factor):
# 计算天体和摄像机的距离
dist = math.sqrt((camera_pos[0] - body_pos[0]) ** 2 +
(camera_pos[1] - body_pos[1]) ** 2 +
(camera_pos[2] - body_pos[2]) ** 2)
# 缩放天体的大小
scaled_dist = dist * scale_factor
# 计算摄像机和天体的连线向量
vector = [body_pos[0] - camera_pos[0], body_pos[1] - camera_pos[1], body_pos[2] - camera_pos[2]]
# 计算单位向量
unit_vector = [vector[0] / dist, vector[1] / dist, vector[2] / dist]
# 根据缩放后的距离和单位向量计算天体的新位置
new_pos = [camera_pos[0] + unit_vector[0] * scaled_dist, camera_pos[1] + unit_vector[1] * scaled_dist,
camera_pos[2] + unit_vector[2] * scaled_dist]
return new_pos
def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): def gen_bodies_from_image(pixel_image, params, texture="color_body.png"):
""" """
根据像素图片以及参数,自动生成星球,注意图片像素不能太多,否则会导致电脑运行太慢 根据像素图片以及参数,自动生成星球,注意图片像素不能太多,否则会导致电脑运行太慢
...@@ -22,9 +39,14 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): ...@@ -22,9 +39,14 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"):
D = 6000 D = 6000
mass = 0.9e25 mass = 0.9e25
camera_pos = params["camera_pos"]
def get_position(pos, scale): def get_position(pos, scale):
# [ 远+近- , 左+右- , 上+下-] # return get_scaled_body_pos((camera_pos[2], camera_pos[0], camera_pos[1]), pos, scale)
return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2] return get_scaled_body_pos((camera_pos[2], camera_pos[1], camera_pos[0]), pos, scale)
# # [ 远+近- , 左+右- , 上+下-]
# return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2]
# return pos[0], pos[1], pos[2] # return pos[0], pos[1], pos[2]
params["ColorBody"] = ColorBody params["ColorBody"] = ColorBody
...@@ -49,9 +71,10 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): ...@@ -49,9 +71,10 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"):
# 以图片像素为坐标,每个像素点到中心的距离 # 以图片像素为坐标,每个像素点到中心的距离
distance_to_center = pow(pow(w - width / 2, 2) + pow(h - height / 2, 2), 1 / 2) distance_to_center = pow(pow(w - width / 2, 2) + pow(h - height / 2, 2), 1 / 2)
# 让 body 从中心开始,离摄像机越远, body 的缩放值越大(scale 就越大,) # 让 body 从中心开始,离摄像机越远, body 的缩放值越大(scale 就越大,)
scale = (distance_to_center / (distance_hw * 10) + 1) # 中心最近 1.0 ~ 1.05 scale = (distance_to_center / (distance_hw * 1.2) + 1) # 中心最近 1.0 ~ 1.25
# scale = scale + (random.randint(100, 200) / 1000)
# TODO: 队列反向排列(中心最远 1.05 ~ 1.0) # TODO: 队列反向排列(中心最远 1.05 ~ 1.0)
# scale = 1.05 - scale + 1.0 # scale = 1.25 - scale + 1.0
# print(scale) # print(scale)
# 获取像素的颜色 # 获取像素的颜色
pixel = img.getpixel((w, h)) pixel = img.getpixel((w, h))
...@@ -66,10 +89,14 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): ...@@ -66,10 +89,14 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"):
return eval(bodies_str, params) return eval(bodies_str, params)
if __name__ == '__main__': def get_scaled_body_pos_test():
import random camera_pos = [0, 0, 0]
from bodies import Body body_pos = [1, 2, 3]
scale_factor = 2
print(get_scaled_body_pos(camera_pos, body_pos, scale_factor))
if __name__ == '__main__':
# get_scaled_body_pos_test()
D = 600 D = 600
mass = 0.9e25 mass = 0.9e25
# camera_pos = 左-右+、上+下-、前+后- # camera_pos = 左-右+、上+下-、前+后-
...@@ -79,14 +106,8 @@ if __name__ == '__main__': ...@@ -79,14 +106,8 @@ if __name__ == '__main__':
def get_position(pos, scale): def get_position(pos, scale):
# [ 远+近- , 左+右- , 上+下-] # [ 远+近- , 左+右- , 上+下-]
return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2] return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2]
# return pos[0], pos[1], pos[2]
bodies: list = gen_bodies_from_image(pixel_image="../images/eye.png", bodies: list = gen_bodies_from_image(pixel_image="../images/eye.png",
params={"D": D, params={"camera_pos": camera_pos})
"Body": Body,
"mass": mass,
"get_position": get_position,
"camera_pos": camera_pos})
print(bodies) print(bodies)
...@@ -44,4 +44,4 @@ if __name__ == '__main__': ...@@ -44,4 +44,4 @@ if __name__ == '__main__':
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后- # position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(0, 2 * AU, -11 * AU)) ursina_run(bodies, SECONDS_PER_YEAR, position=(0, 2 * AU, -11 * AU),show_grid=False)
...@@ -179,81 +179,21 @@ class UrsinaSimulator(Simulator): ...@@ -179,81 +179,21 @@ class UrsinaSimulator(Simulator):
# position=(0, 0, 0), # position=(0, 0, 0),
# rotation=(0, 0, 0)) # rotation=(0, 0, 0))
# def __add_glow(self, entity, intensity=2, light_color=color.white, attenuation=3):
# """
# 未用,保留代码
# :param entity:
# :param intensity:
# :param light_color:
# :param attenuation:
# :return:
# """
# lights = []
# import math
# for i in range(5):
# glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
# scale=math.pow(1.03, i), alpha=0.2)
# lights.append(glow_entity)
# # 创建一个新的 Entity 对象,作为光晕的容器
# # glow_entity = Entity(parent=entity, model='sphere', scale=entity.scale * 1.2)
# # 创建 PointLight 对象,并设置它的属性
# for i in range(2):
# light = PointLight(parent=lights[0], intensity=intensity, color=light_color, attenuation=attenuation)
# lights.append(light)
#
# # 把 Entity 对象放到星星的后面,使得光晕看起来像是从星星发出来的
# glow_entity.world_position = entity.world_position
# glow_entity.world_parent = entity.parent
# glow_entity.y += entity.scale_y * 0.1
# glow_entity.depth_test = False
# return lights
# def create_fixed_star_lights(self, entity):
# """
# 创建恒星的发光的效果、并作为灯光源
# :param entity:
# :return:
# """
#
# # 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
# entity.set_light_off()
#
# lights = []
# # 创建多个新的 Entity 对象,作为光晕的容器
# for i in range(10):
# glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
# scale=math.pow(1.03, i), alpha=0.1)
#
# lights.append(glow_entity)
# for i in range(2):
# # 创建 PointLight 对象,作为恒星的灯光源
# light = PointLight(parent=entity, intensity=10, range=10, color=color.white)
# lights.append(light)
#
# # light = DirectionalLight(shadows=True, direction=Vec3(0, 0, 1), color=color.white)
# # light.look_at(Vec3(0, 0, -1))
# # light = SpotLight(parent=entity,shadows=True, direction=Vec3(1,1,1), color=color.white)
#
# return lights
def run(self, dt, **kwargs): def run(self, dt, **kwargs):
window.title = '宇宙模拟器' window.title = '宇宙模拟器'
# 默认非近距离查看
view_closely = False view_closely = False
if "view_closely" in kwargs: if "view_closely" in kwargs:
view_closely = kwargs["view_closely"] view_closely = kwargs["view_closely"]
if view_closely: if view_closely:
# 近距离查看
# 设置 camera 的裁剪面和位置 # 设置 camera 的裁剪面和位置
camera.clip_plane_near = 0.01 camera.clip_plane_near = 0.01
camera.fov = 60 camera.fov = 60
# 一定要够大,如果小于 Sky(texture=texture).scale = 50000,宇宙背景就会出现黑色方洞
# camera.clip_plane_far = 100000
# camera.position = (0, 10, -20)
# camera.rotation_x = -30
# interval_fator 能让更新天体运行状态(位置、速度)更精确 # interval_fator 能让更新天体运行状态(位置、速度)更精确
# 设定时间间隔为0.01秒 # 设定时间间隔为0.01秒
self.interval_fator = 0.01 self.interval_fator = 0.01
...@@ -263,13 +203,6 @@ class UrsinaSimulator(Simulator): ...@@ -263,13 +203,6 @@ class UrsinaSimulator(Simulator):
self.interval = datetime.timedelta(seconds=self.interval_fator) self.interval = datetime.timedelta(seconds=self.interval_fator)
self.last_time = datetime.datetime.now() - datetime.timedelta(seconds=2) self.last_time = datetime.datetime.now() - datetime.timedelta(seconds=2)
if "light" in kwargs:
if kwargs["light"]:
for v in self.ursina_views:
if v.body.is_fixed_star:
# self.lights = self.create_fixed_star_lights(v.planet)
pass
if "show_grid" in kwargs: if "show_grid" in kwargs:
if kwargs["show_grid"]: if kwargs["show_grid"]:
WorldGrid() WorldGrid()
...@@ -309,8 +242,8 @@ class UrsinaSimulator(Simulator): ...@@ -309,8 +242,8 @@ class UrsinaSimulator(Simulator):
bg_music = find_file(bg_music) bg_music = find_file(bg_music)
if bg_music is None: if bg_music is None:
# bg_music = "../sounds/universe_04.mp3" # bg_music = "../sounds/universe_04.mp3" # 默认背景音乐
bg_music = "../none" bg_music = "../none" # 默认没有背景音乐
if os.path.exists(bg_music): if os.path.exists(bg_music):
audio = Audio(bg_music, pitch=1, loop=True, autoplay=True) audio = Audio(bg_music, pitch=1, loop=True, autoplay=True)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册