diff --git a/html-renderer/main.py b/html-renderer/main.py
new file mode 100644
index 0000000..b82a3c7
--- /dev/null
+++ b/html-renderer/main.py
@@ -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
, 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 = """
+
+
+ Hello, Pygame HTML Renderer!
+ This is a proof of concept for rendering HTML with CSS in Pygame.
+ This paragraph is red!
+ This paragraph is larger.
+ This paragraph has margins!
+ Scroll down for more content!
+
+
+ """
+
+ # 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()
diff --git a/physicaly-based-renderer/aillian.mat b/physicaly-based-renderer/aillian.mat
new file mode 100644
index 0000000..d3caf0b
--- /dev/null
+++ b/physicaly-based-renderer/aillian.mat
@@ -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
\ No newline at end of file
diff --git a/physicaly-based-renderer/bricks.mat b/physicaly-based-renderer/bricks.mat
new file mode 100644
index 0000000..2643c15
--- /dev/null
+++ b/physicaly-based-renderer/bricks.mat
@@ -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
\ No newline at end of file
diff --git a/physicaly-based-renderer/imgui.ini b/physicaly-based-renderer/imgui.ini
index ce9f901..a3c5777 100644
--- a/physicaly-based-renderer/imgui.ini
+++ b/physicaly-based-renderer/imgui.ini
@@ -4,7 +4,7 @@ Size=400,400
Collapsed=0
[Window][Scene Controls]
-Pos=7,4
-Size=334,466
+Pos=13,10
+Size=484,570
Collapsed=0
diff --git a/physicaly-based-renderer/main.cpp b/physicaly-based-renderer/main.cpp
index 254a5bf..00c51ea 100644
--- a/physicaly-based-renderer/main.cpp
+++ b/physicaly-based-renderer/main.cpp
@@ -1,11 +1,14 @@
// main.cpp
-// A minimal physically based renderer with ImGui integration,
-// normal mapping (with per-vertex tangents), YAML-based material loading/saving,
-// and support for loading 3D models (OBJ files) via tinyobjloader.
-// Uses OpenGL, GLFW, GLEW, GLM, ImGui (and IM_PI), stb_image, yaml-cpp, and tinyobjloader.
-// Compile (e.g. on Linux) with:
-// g++ main.cpp -lglfw -lGLEW -lGL -ldl -limgui -lyaml-cpp -o pbr_renderer
-// (Adjust include paths and linker flags as needed.)
+//
+// A physically based renderer that demonstrates height mapping via parallax,
+// improved lighting (point & directional lights), and renders a skybox for background.
+// It uses procedural spheres and a plane. Materials (including height maps) can be loaded
+// and saved via YAML (using yaml-cpp) and are editable via ImGui.
+//
+// Compile (on Linux):
+// g++ main.cpp -lglfw -lGLEW -lGL -ldl -limgui -lyaml-cpp -o pbr_renderer
+//
+// Adjust library paths and names as needed.
#include
#include
@@ -20,11 +23,11 @@
#include
#include
#include
-#include