Added Cubemaps and reflections
This commit is contained in:
parent
41dcb86dbb
commit
7a68447085
254
html-renderer/main.py
Normal file
254
html-renderer/main.py
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import sys
|
||||||
|
import pygame
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup, NavigableString
|
||||||
|
|
||||||
|
def parse_css(css_text):
|
||||||
|
"""
|
||||||
|
A simple CSS parser mapping selectors to a dictionary
|
||||||
|
of property names and values.
|
||||||
|
"""
|
||||||
|
styles = {}
|
||||||
|
css_text = css_text.replace("\n", "")
|
||||||
|
blocks = css_text.split("}")
|
||||||
|
for block in blocks:
|
||||||
|
if "{" in block:
|
||||||
|
selector, props = block.split("{", 1)
|
||||||
|
selector = selector.strip()
|
||||||
|
props = props.strip()
|
||||||
|
prop_dict = {}
|
||||||
|
for declaration in props.split(";"):
|
||||||
|
if ":" in declaration:
|
||||||
|
key, value = declaration.split(":", 1)
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip()
|
||||||
|
# For font-size, store the raw value so percentages can be processed.
|
||||||
|
if key == "font-size":
|
||||||
|
prop_dict[key] = value
|
||||||
|
else:
|
||||||
|
prop_dict[key] = value
|
||||||
|
styles[selector] = prop_dict
|
||||||
|
return styles
|
||||||
|
|
||||||
|
def get_computed_style(element, css_styles):
|
||||||
|
"""
|
||||||
|
Merge CSS rules (by tag name) with inline style declarations.
|
||||||
|
"""
|
||||||
|
style = {}
|
||||||
|
if element.name in css_styles:
|
||||||
|
style.update(css_styles[element.name])
|
||||||
|
if 'style' in element.attrs:
|
||||||
|
declarations = element['style'].split(";")
|
||||||
|
for decl in declarations:
|
||||||
|
if ":" in decl:
|
||||||
|
key, value = decl.split(":", 1)
|
||||||
|
style[key.strip()] = value.strip()
|
||||||
|
return style
|
||||||
|
|
||||||
|
def render_element(surface, element, x, y, css_styles, base_font):
|
||||||
|
"""
|
||||||
|
Recursively render an element (or text node) on the surface.
|
||||||
|
Returns the total height rendered.
|
||||||
|
"""
|
||||||
|
total_height = 0
|
||||||
|
|
||||||
|
# Handle text nodes.
|
||||||
|
if isinstance(element, NavigableString):
|
||||||
|
text = str(element).strip()
|
||||||
|
if text:
|
||||||
|
text_surface = base_font.render(text, True, pygame.Color("black"))
|
||||||
|
surface.blit(text_surface, (x, y))
|
||||||
|
return text_surface.get_height()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# For tag nodes, compute the style.
|
||||||
|
computed_style = get_computed_style(element, css_styles)
|
||||||
|
|
||||||
|
# Handle margins (defaults: top=0, bottom=5).
|
||||||
|
try:
|
||||||
|
margin_top = int(computed_style.get("margin-top", "0"))
|
||||||
|
except ValueError:
|
||||||
|
margin_top = 0
|
||||||
|
try:
|
||||||
|
margin_bottom = int(computed_style.get("margin-bottom", "5"))
|
||||||
|
except ValueError:
|
||||||
|
margin_bottom = 5
|
||||||
|
|
||||||
|
total_height += margin_top
|
||||||
|
|
||||||
|
# Process font size.
|
||||||
|
fs_val = computed_style.get("font-size", None)
|
||||||
|
if fs_val:
|
||||||
|
fs_val = fs_val.strip()
|
||||||
|
if fs_val.endswith('%'):
|
||||||
|
try:
|
||||||
|
percent = float(fs_val.strip('%'))
|
||||||
|
font_size = int(base_font.get_height() * percent / 100.0)
|
||||||
|
except Exception:
|
||||||
|
font_size = base_font.get_height()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
font_size = int(fs_val)
|
||||||
|
except ValueError:
|
||||||
|
font_size = base_font.get_height()
|
||||||
|
else:
|
||||||
|
font_size = base_font.get_height()
|
||||||
|
|
||||||
|
# Process color, with fallback.
|
||||||
|
color_str = computed_style.get("color", "black")
|
||||||
|
try:
|
||||||
|
color = pygame.Color(color_str)
|
||||||
|
except ValueError:
|
||||||
|
color = pygame.Color("black")
|
||||||
|
|
||||||
|
# Create a new font with the computed font size.
|
||||||
|
font = pygame.font.SysFont(None, font_size)
|
||||||
|
# Apply simple tag-based styling.
|
||||||
|
if element.name == "b":
|
||||||
|
font.set_bold(True)
|
||||||
|
elif element.name == "i":
|
||||||
|
font.set_italic(True)
|
||||||
|
|
||||||
|
# Recursively render children.
|
||||||
|
for child in element.children:
|
||||||
|
child_height = render_element(surface, child, x, y + total_height, css_styles, font)
|
||||||
|
total_height += child_height
|
||||||
|
|
||||||
|
total_height += margin_bottom
|
||||||
|
return total_height
|
||||||
|
|
||||||
|
def render_html(surface, html, css_text):
|
||||||
|
"""
|
||||||
|
Renders the HTML content onto a Pygame surface.
|
||||||
|
Returns the total height used.
|
||||||
|
"""
|
||||||
|
css_styles = parse_css(css_text)
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
|
||||||
|
# Remove tags that are non-renderable.
|
||||||
|
for tag in soup.find_all(["script", "style", "meta", "link", "head"]):
|
||||||
|
tag.decompose()
|
||||||
|
|
||||||
|
y = 10
|
||||||
|
base_font = pygame.font.SysFont(None, 24)
|
||||||
|
|
||||||
|
# Prefer content inside <body>, if available.
|
||||||
|
body = soup.find("body")
|
||||||
|
if body:
|
||||||
|
for element in body.children:
|
||||||
|
y += render_element(surface, element, 10, y, css_styles, base_font)
|
||||||
|
else:
|
||||||
|
for element in soup.contents:
|
||||||
|
y += render_element(surface, element, 10, y, css_styles, base_font)
|
||||||
|
return y
|
||||||
|
|
||||||
|
def fetch_html(url):
|
||||||
|
"""
|
||||||
|
Download the HTML content from a URL.
|
||||||
|
"""
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.text
|
||||||
|
|
||||||
|
def render_html_to_surface(html, css_text, width):
|
||||||
|
"""
|
||||||
|
Render the HTML to an offscreen surface for scrolling.
|
||||||
|
"""
|
||||||
|
# Create a temporary surface with extra height.
|
||||||
|
temp_surface = pygame.Surface((width, 3000))
|
||||||
|
rendered_height = render_html(temp_surface, html, css_text)
|
||||||
|
|
||||||
|
# If nothing was rendered (common with complex pages), fallback to plain text.
|
||||||
|
if rendered_height < 20:
|
||||||
|
plain_text = BeautifulSoup(html, "html.parser").get_text()
|
||||||
|
temp_surface.fill((255, 255, 255))
|
||||||
|
font = pygame.font.SysFont(None, 24)
|
||||||
|
y = 10
|
||||||
|
for line in plain_text.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
text_surface = font.render(line, True, pygame.Color("black"))
|
||||||
|
temp_surface.blit(text_surface, (10, y))
|
||||||
|
y += text_surface.get_height() + 5
|
||||||
|
rendered_height = y
|
||||||
|
|
||||||
|
# Create a surface exactly as tall as the rendered content.
|
||||||
|
content_surface = pygame.Surface((width, rendered_height))
|
||||||
|
content_surface.blit(temp_surface, (0, 0))
|
||||||
|
return content_surface, rendered_height
|
||||||
|
|
||||||
|
def main():
|
||||||
|
pygame.init()
|
||||||
|
screen_width, screen_height = 800, 600
|
||||||
|
screen = pygame.display.set_mode((screen_width, screen_height))
|
||||||
|
pygame.display.set_caption("HTML Renderer with URL Fetch, CSS, and Scrolling")
|
||||||
|
|
||||||
|
# Fetch HTML from a URL if provided; otherwise, use sample HTML.
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
url = sys.argv[1]
|
||||||
|
try:
|
||||||
|
html = fetch_html(url)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error fetching {url}: {e}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
html = """
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, Pygame HTML Renderer!</h1>
|
||||||
|
<p>This is a <b>proof of concept</b> for rendering <i>HTML</i> with CSS in Pygame.</p>
|
||||||
|
<p style="color: red;">This paragraph is red!</p>
|
||||||
|
<p style="font-size: 150;">This paragraph is larger.</p>
|
||||||
|
<p style="margin-top: 20; margin-bottom: 20;">This paragraph has margins!</p>
|
||||||
|
<p>Scroll down for more content!</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# A minimal default CSS.
|
||||||
|
css_text = """
|
||||||
|
h1 {
|
||||||
|
font-size: 36;
|
||||||
|
color: blue;
|
||||||
|
margin-bottom: 10;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 24;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Render the HTML to an offscreen content surface.
|
||||||
|
content_surface, content_height = render_html_to_surface(html, css_text, screen_width)
|
||||||
|
scroll_offset = 0
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
max_scroll = max(0, content_height - screen_height)
|
||||||
|
|
||||||
|
running = True
|
||||||
|
while running:
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
running = False
|
||||||
|
|
||||||
|
# Handle keyboard scrolling.
|
||||||
|
if event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == pygame.K_DOWN:
|
||||||
|
scroll_offset = min(max_scroll, scroll_offset + 20)
|
||||||
|
elif event.key == pygame.K_UP:
|
||||||
|
scroll_offset = max(0, scroll_offset - 20)
|
||||||
|
# Handle mouse wheel scrolling.
|
||||||
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
if event.button == 4: # Wheel up.
|
||||||
|
scroll_offset = max(0, scroll_offset - 20)
|
||||||
|
elif event.button == 5: # Wheel down.
|
||||||
|
scroll_offset = min(max_scroll, scroll_offset + 20)
|
||||||
|
|
||||||
|
screen.fill((255, 255, 255))
|
||||||
|
screen.blit(content_surface, (0, -scroll_offset))
|
||||||
|
pygame.display.flip()
|
||||||
|
clock.tick(30)
|
||||||
|
|
||||||
|
pygame.quit()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
10
physicaly-based-renderer/aillian.mat
Normal file
10
physicaly-based-renderer/aillian.mat
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
albedo: [1, 1, 1]
|
||||||
|
metallic: 0
|
||||||
|
roughness: 0.0500000007
|
||||||
|
ao: 0
|
||||||
|
albedo_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_albedo.png
|
||||||
|
metallic_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_metallic.png
|
||||||
|
roughness_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_roughness.png
|
||||||
|
ao_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_ao.png
|
||||||
|
normal_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_normal-ogl.png
|
||||||
|
height_texture: C:\Users\spenc\Downloads\alien-carnivorous-plant-bl\alien-carnivorous-plant-bl\alien-carniverous-plant_height.png
|
9
physicaly-based-renderer/bricks.mat
Normal file
9
physicaly-based-renderer/bricks.mat
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
albedo: [1, 0, 0]
|
||||||
|
metallic: 1
|
||||||
|
roughness: 1
|
||||||
|
ao: 1
|
||||||
|
albedo_texture: C:\Users\spenc\Downloads\alley-brick-wall-bl\alley-brick-wall-bl\alley-brick-wall_albedo.png
|
||||||
|
metallic_texture: C:\Users\spenc\Downloads\alley-brick-wall-bl\alley-brick-wall-bl\alley-brick-wall_metallic.png
|
||||||
|
roughness_texture: C:\Users\spenc\Downloads\alley-brick-wall-bl\alley-brick-wall-bl\alley-brick-wall_roughness.png
|
||||||
|
ao_texture: C:\Users\spenc\Downloads\alley-brick-wall-bl\alley-brick-wall-bl\alley-brick-wall_ao.png
|
||||||
|
normal_texture: C:\Users\spenc\Downloads\alley-brick-wall-bl\alley-brick-wall-bl\alley-brick-wall_normal-ogl.png
|
@ -4,7 +4,7 @@ Size=400,400
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Scene Controls]
|
[Window][Scene Controls]
|
||||||
Pos=7,4
|
Pos=13,10
|
||||||
Size=334,466
|
Size=484,570
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -6,4 +6,5 @@ albedo_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-
|
|||||||
metallic_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_metallic.png
|
metallic_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_metallic.png
|
||||||
roughness_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_roughness.png
|
roughness_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_roughness.png
|
||||||
ao_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_ao.png
|
ao_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_ao.png
|
||||||
normal_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_normal-ogl.png
|
normal_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_normal-ogl.png
|
||||||
|
height_texture: C:\Users\spenc\Downloads\used-stainless-steel-bl\used-stainless-steel-bl\used-stainless-steel_height.png
|
BIN
physicaly-based-renderer/skybox/back.jpg
Normal file
BIN
physicaly-based-renderer/skybox/back.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 723 KiB |
BIN
physicaly-based-renderer/skybox/bottom.jpg
Normal file
BIN
physicaly-based-renderer/skybox/bottom.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 274 KiB |
BIN
physicaly-based-renderer/skybox/front.jpg
Normal file
BIN
physicaly-based-renderer/skybox/front.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 462 KiB |
BIN
physicaly-based-renderer/skybox/left.jpg
Normal file
BIN
physicaly-based-renderer/skybox/left.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 588 KiB |
BIN
physicaly-based-renderer/skybox/right.jpg
Normal file
BIN
physicaly-based-renderer/skybox/right.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 525 KiB |
BIN
physicaly-based-renderer/skybox/top.jpg
Normal file
BIN
physicaly-based-renderer/skybox/top.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 338 KiB |
Loading…
Reference in New Issue
Block a user