Skip to content

Commit de89790

Browse files
richlib: simple API on top of raylib
1 parent c3d018c commit de89790

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

raylib/richlib/__init__.py

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
""" Richlib, simple access to Raylib
2+
"""
3+
__version__ = '0.1'
4+
5+
from ..dynamic import ffi, raylib as rl
6+
from ..colors import *
7+
8+
import sys
9+
10+
data_dir = ""
11+
12+
camera = ffi.new("struct Camera3D *")
13+
camera.position = (0.0, 100, 100)
14+
camera.target = (0.0, 0.0, 0.0)
15+
camera.up = (0, 1, 0)
16+
camera.fovy = 45
17+
camera.type = rl.CAMERA_PERSPECTIVE
18+
19+
mod = sys.modules['__main__']
20+
21+
22+
class Vector(list):
23+
@property
24+
def x(self):
25+
return self[0]
26+
27+
@x.setter
28+
def x(self, value):
29+
self[0] = value
30+
31+
@property
32+
def y(self):
33+
return self[1]
34+
35+
@y.setter
36+
def y(self, value):
37+
self[1] = value
38+
39+
@property
40+
def z(self):
41+
return self[2]
42+
43+
@z.setter
44+
def z(self, value):
45+
self[2] = value
46+
47+
48+
def make_color(c):
49+
if isinstance(c, str):
50+
return globals()[c.upper()]
51+
else:
52+
return c
53+
54+
55+
def clear(color=BLACK):
56+
rl.ClearBackground(color)
57+
58+
59+
class Shape:
60+
@property
61+
def color(self):
62+
return self._color
63+
64+
@color.setter
65+
def color(self, value):
66+
if isinstance(value, str):
67+
self._color = globals()[value.upper()]
68+
else:
69+
self._color = value
70+
71+
@property
72+
def pos(self):
73+
return self._pos
74+
75+
@pos.setter
76+
def pos(self, value):
77+
self._pos = Vector(value)
78+
79+
@property
80+
def wire_color(self):
81+
return self._wire_color
82+
83+
@wire_color.setter
84+
def wire_color(self, value):
85+
if isinstance(value, str):
86+
self._wire_color = globals()[value.upper()]
87+
else:
88+
self._wire_color = value
89+
90+
@property
91+
def x(self):
92+
return self.pos.x
93+
94+
@x.setter
95+
def x(self, value):
96+
self.pos.x = value
97+
98+
@property
99+
def y(self):
100+
return self.pos.y
101+
102+
@y.setter
103+
def y(self, value):
104+
self.pos.y = value
105+
106+
@property
107+
def z(self):
108+
return self.pos.z
109+
110+
@z.setter
111+
def z(self, value):
112+
self.pos.z = value
113+
114+
115+
class Box(Shape):
116+
def __init__(self, position, size, color=RED, wires=True, wire_color=DARKGRAY):
117+
self.pos = position
118+
self.size = size
119+
self.color = color
120+
self.wire_color = wire_color # make_color(wire_color)
121+
self.wires = wires
122+
123+
# def __getattr__(self, item):
124+
# return self.pos.item
125+
#
126+
# def __setattr__(self, key, value):
127+
# self.pos.key = value
128+
129+
@property
130+
def size(self):
131+
return self._size
132+
133+
@size.setter
134+
def size(self, value):
135+
print("setting size ", value, type(value))
136+
self._size = Vector(value)
137+
138+
def get_bounding_box(self):
139+
return (
140+
(
141+
self.pos.x - self.size.x / 2,
142+
self.pos.y - self.size.y / 2,
143+
self.pos.z - self.size.z / 2,
144+
),
145+
(
146+
self.pos.x + self.size.x / 2,
147+
self.pos.y + self.size.y / 2,
148+
self.pos.z + self.size.z / 2,
149+
)
150+
)
151+
152+
def draw(self):
153+
rl.DrawCubeV(self.pos, self.size, self.color)
154+
if self.wires:
155+
rl.DrawCubeWiresV(
156+
self.pos, self.size, self.wire_color
157+
)
158+
159+
def check_collision(self, other):
160+
if isinstance(other, Sphere):
161+
r = rl.CheckCollisionBoxSphere(
162+
self.get_bounding_box(), other.pos, other.radius
163+
)
164+
return r
165+
elif isinstance(other, Box):
166+
return rl.CheckCollisionBoxes(self.get_bounding_box(), other.get_bounding_box())
167+
elif isinstance(other, Actor):
168+
return self.check_collision(other.collision_sphere)
169+
170+
171+
class Sphere(Shape):
172+
def __init__(self, position, radius, color=RED, wires=True, wire_color=DARKGRAY):
173+
self.pos = position
174+
self.radius = radius
175+
self.color = color
176+
self.wire_color = wire_color
177+
self.wires = wires
178+
179+
def draw(self):
180+
rl.DrawSphere(self.pos, self.radius, self.color)
181+
if self.wires:
182+
rl.DrawSphereWires(self.pos, self.radius, 32, 32, self.wire_color)
183+
184+
def check_collision(self, other):
185+
if isinstance(other, Sphere):
186+
return rl.CheckCollisionSpheres(
187+
self.pos, self.radius, other.pos, other.radius
188+
)
189+
elif isinstance(other, Box):
190+
return rl.CheckCollisionBoxSphere(
191+
other.get_bounding_box(), self.pos, self.radius
192+
)
193+
elif isinstance(other, Actor):
194+
return self.check_collision(other.collision_sphere)
195+
196+
197+
class Actor(Shape):
198+
def __init__(self, model_file, position=Vector([0, 0, 0]), collision_radius=1, texture_file="",
199+
rotation_axis=Vector([0, 1, 0]), rotation_angle=0, scale=Vector([1, 1, 1]), color=WHITE, wires=False,
200+
wire_color=DARKGRAY):
201+
#super().__init__(position, collision_radius, color, wires, wire_color)
202+
203+
self.pos = position
204+
self.color = color
205+
self.wire_color = wire_color
206+
self.wires = wires
207+
208+
self.model_file = model_file
209+
if texture_file == "":
210+
texture_file = model_file + "_diffuse"
211+
self.texture_file = texture_file
212+
self.scale = scale
213+
self.rotation_axis = rotation_axis
214+
self.rotation_angle = rotation_angle
215+
self.collision_radius = collision_radius
216+
self.collision_sphere = Sphere(position, collision_radius, RED)
217+
218+
self.loaded = False
219+
220+
def load_data(self):
221+
self.loaded = True
222+
print("*** DATA_DIR=", data_dir)
223+
self.model = rl.LoadModel((data_dir + self.model_file + '.obj').encode('utf-8'))
224+
texture = rl.LoadTexture((data_dir + self.texture_file + '.png').encode('utf-8'))
225+
self.model.materials[0].maps[rl.MAP_DIFFUSE].texture = texture
226+
227+
def calc_bounding_box(self):
228+
bb = rl.MeshBoundingBox(self.model.meshes[0])
229+
230+
bb.min.x *= self.scale.x
231+
bb.min.y *= self.scale.y
232+
bb.min.z *= self.scale.z
233+
bb.max.x *= self.scale.x
234+
bb.max.y *= self.scale.y
235+
bb.max.z *= self.scale.z
236+
237+
bb.min.x += self.pos.x
238+
bb.min.y += self.pos.y
239+
bb.min.z += self.pos.z
240+
bb.max.x += self.pos.x
241+
bb.max.y += self.pos.y
242+
bb.max.z += self.pos.z
243+
return bb
244+
245+
def calc_collision_sphere(self):
246+
centre_x = (self.bounding_box.max.x + self.bounding_box.min.x)/2
247+
centre_y = (self.bounding_box.max.y + self.bounding_box.min.y)/2
248+
centre_z = (self.bounding_box.max.z + self.bounding_box.min.z)/2
249+
sphere = Sphere((centre_x, centre_y, centre_z), self.collision_radius, RED)
250+
return sphere
251+
252+
def draw(self):
253+
if not self.loaded:
254+
self.load_data()
255+
self.bounding_box = self.calc_bounding_box()
256+
self.collision_sphere = self.calc_collision_sphere()
257+
rl.DrawModelEx(self.model, self.pos, self.rotation_axis, self.rotation_angle, self.scale, self.color)
258+
if self.wires:
259+
rl.DrawBoundingBox(self.bounding_box, self.wire_color)
260+
self.collision_sphere.draw()
261+
262+
def check_collision(self, other):
263+
return self.collision_sphere.check_collision(other)
264+
265+
def run():
266+
screen_width = 800
267+
screen_height = 450
268+
title = "RayLibPyZero"
269+
270+
if hasattr(mod, "WIDTH"):
271+
screen_width = mod.WIDTH
272+
273+
if hasattr(mod, "HEIGHT"):
274+
screen_height = mod.HEIGHT
275+
276+
if hasattr(mod, "TITLE"):
277+
title = mod.TITLE
278+
279+
if hasattr(mod, "DATA_DIR"):
280+
global data_dir
281+
data_dir = mod.DATA_DIR
282+
283+
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT + rl.FLAG_MSAA_4X_HINT)
284+
285+
rl.InitWindow(screen_width, screen_height, title.encode('utf-8'))
286+
287+
rl.SetTargetFPS(60)
288+
289+
if hasattr(mod, "CAMERA"):
290+
rl.SetCameraMode(camera, mod.CAMERA)
291+
292+
if hasattr(mod, "init"):
293+
mod.init()
294+
295+
while not rl.WindowShouldClose():
296+
if hasattr(mod, "update"):
297+
mod.update()
298+
rl.UpdateCamera(camera)
299+
if rl.IsKeyPressed(rl.KEY_F):
300+
rl.ToggleFullscreen()
301+
if rl.IsKeyPressed(rl.KEY_ESCAPE):
302+
rl.Exit()
303+
rl.BeginDrawing()
304+
rl.BeginMode3D(camera[0])
305+
if hasattr(mod, "draw3d"):
306+
mod.draw3d()
307+
rl.EndMode3D()
308+
if hasattr(mod, "draw2d"):
309+
mod.draw2d()
310+
if hasattr(mod, "draw"):
311+
mod.draw()
312+
rl.EndDrawing()
313+
rl.CloseWindow()
314+
315+
316+
class Keyboard:
317+
def __getattr__(self, kname):
318+
# return is a reserved word, so alias enter to return
319+
if kname == 'enter':
320+
kname = 'return'
321+
kname = kname.upper()
322+
if not kname.startswith("KEY_"):
323+
kname = "KEY_" + kname
324+
325+
return rl.IsKeyDown(getattr(rl, kname))
326+
327+
328+
keyboard = Keyboard()

test_richlib.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from raylib.richlib import *
2+
3+
WIDTH=800
4+
HEIGHT=640
5+
#CAMERA=CAMERA_FIRST_PERSON
6+
DATA_DIR="examples/models/resources/models/"
7+
8+
player = Box((0, 10, 20), (10, 20, 10), GREEN)
9+
enemy_box = Box((-40, 10, 0), (20, 20, 20), (70,70,70))
10+
enemy_sphere = Sphere((40, 15, 0), 15, 'gray')
11+
castle = Actor("castle", collision_radius=15)
12+
castle.z=-50
13+
castle.scale.y=2
14+
castle.scale.x=1
15+
castle.scale.z=1
16+
17+
#def init():
18+
# set_camera_mode(camera, CAMERA_FIRST_PERSON)
19+
20+
def update():
21+
if keyboard.right:
22+
player.pos.x += 2
23+
elif keyboard.left:
24+
player.pos.x -= 2
25+
elif keyboard.down:
26+
player.pos.z += 2
27+
elif keyboard.up:
28+
player.pos.z -= 2
29+
30+
if player.check_collision(enemy_box) or player.check_collision(enemy_sphere) or player.check_collision(castle):
31+
player.color = RED
32+
else:
33+
player.color = GREEN
34+
35+
36+
def draw3d():
37+
rl.ClearBackground(WHITE)
38+
enemy_box.draw()
39+
enemy_sphere.draw()
40+
player.draw()
41+
castle.draw()
42+
rl.DrawGrid(10, 10)
43+
44+
45+
def draw2d():
46+
rl.DrawText(b"Move player with cursors to collide", 220, 40, 20, GRAY)
47+
rl.DrawFPS(10, 10)
48+
49+
50+
run()

0 commit comments

Comments
 (0)