Skip to content

Commit b75ec11

Browse files
v0.3.8: PBR High-Fidelity Overhaul, Albedo Mapping fix, and Context Menu Occlusion fix
1 parent 8c17393 commit b75ec11

18 files changed

Lines changed: 43084 additions & 76 deletions

demo/2026-3-22 12-39-1.jpg

448 KB
Loading

p3deditor/CommandInterpreter.pde

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class CommandInterpreter {
7373
String hexStr = parts.get(2).replace("#", "");
7474
if (hexStr.length() == 6) {
7575
e.col = (int)Long.parseLong("FF" + hexStr, 16);
76+
e.material.albedo = e.col;
7677
return "SUCCESS: Colored " + e.name;
7778
}
7879
return "Error: Invalid hex format";
@@ -89,6 +90,20 @@ class CommandInterpreter {
8990
}
9091
return "SUCCESS: Scaled " + e.name;
9192
}
93+
else if (rawCmd.equals("metallic") || rawCmd.equals("metal")) {
94+
if (parts.size() < 3) return "Error: metal <name> <val>";
95+
Entity e = findEntity(parts.get(1));
96+
if (e == null) return "Error: Entity not found: " + parts.get(1);
97+
e.material.metallic = float(parts.get(2));
98+
return "SUCCESS: Set metallic of " + e.name + " to " + e.material.metallic;
99+
}
100+
else if (rawCmd.equals("roughness") || rawCmd.equals("rough")) {
101+
if (parts.size() < 3) return "Error: rough <name> <val>";
102+
Entity e = findEntity(parts.get(1));
103+
if (e == null) return "Error: Entity not found: " + parts.get(1);
104+
e.material.roughness = float(parts.get(2));
105+
return "SUCCESS: Set roughness of " + e.name + " to " + e.material.roughness;
106+
}
92107
else if (rawCmd.equals("delete") || rawCmd.equals("remove")) {
93108
if (parts.size() < 2) return "Error: delete <name>";
94109
Entity e = findEntity(parts.get(1));
@@ -236,8 +251,42 @@ class CommandInterpreter {
236251
p3deditor.this.oscTelemetryEnabled = parts.get(1).equalsIgnoreCase("on");
237252
return "SUCCESS: OSC Telemetry is now " + (p3deditor.this.oscTelemetryEnabled ? "ON" : "OFF");
238253
}
254+
else if (rawCmd.equals("load_obj")) {
255+
if (parts.size() < 3) return "Error: load_obj <name> <path>";
256+
Entity e = findEntity(parts.get(1));
257+
if (e == null) return "Error: Entity not found: " + parts.get(1);
258+
e.model = p3deditor.this.loadShape(parts.get(2));
259+
e.type = "Model";
260+
return "SUCCESS: Loaded model into " + e.name;
261+
}
262+
else if (rawCmd.equals("load_env")) {
263+
if (parts.size() < 2) return "Error: load_env <path>";
264+
scene.envMap = p3deditor.this.loadImage(parts.get(1));
265+
return "SUCCESS: Loaded Global Environment Map: " + parts.get(1);
266+
}
267+
else if (rawCmd.equals("load_albedo")) {
268+
if (parts.size() < 3) return "Error: load_albedo <name> <path>";
269+
Entity e = findEntity(parts.get(1));
270+
if (e == null) return "Error: Entity not found: " + parts.get(1);
271+
e.material.setAlbedoMap(p3deditor.this.loadImage(parts.get(2)));
272+
return "SUCCESS: Loaded Albedo Map into " + e.name;
273+
}
274+
else if (rawCmd.equals("load_metal")) {
275+
if (parts.size() < 3) return "Error: load_metal <name> <path>";
276+
Entity e = findEntity(parts.get(1));
277+
if (e == null) return "Error: Entity not found: " + parts.get(1);
278+
e.material.setMetallicMap(p3deditor.this.loadImage(parts.get(2)));
279+
return "SUCCESS: Loaded Metallic Map into " + e.name;
280+
}
281+
else if (rawCmd.equals("load_rough")) {
282+
if (parts.size() < 3) return "Error: load_rough <name> <path>";
283+
Entity e = findEntity(parts.get(1));
284+
if (e == null) return "Error: Entity not found: " + parts.get(1);
285+
e.material.setRoughnessMap(p3deditor.this.loadImage(parts.get(2)));
286+
return "SUCCESS: Loaded Roughness Map into " + e.name;
287+
}
239288
else if (rawCmd.equals("help")) {
240-
return "CMDS: move, tp, color, scale, delete, rename, clear, echo, play, stop, mount, unmount, alias, unalias, exec, run, osc_connect, osc_send, osc_telemetry, help";
289+
return "CMDS: move, tp, color, scale, delete, rename, clear, create, metal, rough, load_obj, load_env, load_albedo, mount, osc_connect, play, help";
241290
}
242291
} catch (Exception ex) {
243292
return "Error: " + ex.getMessage();

p3deditor/Entity.pde

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ class Entity {
55
Transform transform;
66
boolean selected = false;
77
color col = color(200);
8+
Material material = new Material();
89
Entity parent = null;
910
ArrayList<Entity> children = new ArrayList<Entity>();
1011

1112
// Point Light Specific Properties
1213
float lightIntensity = 1.0f;
1314
float lightRange = 300.0f;
1415

16+
// v0.8.0: External Model Slot
17+
PShape model = null;
18+
1519
// v0.5.0: Events & Scripts mounting
1620
HashMap<String, ArrayList<String>> eventHandlers = new HashMap<String, ArrayList<String>>();
1721

@@ -36,6 +40,7 @@ class Entity {
3640
java.awt.Color c = java.awt.Color.getHSBColor(h, 0.45f, 0.85f);
3741
this.col = color(c.getRed(), c.getGreen(), c.getBlue());
3842
}
43+
this.material.albedo = this.col;
3944
}
4045

4146
void setParent(Entity newParent, boolean preserveWorld) {
@@ -75,6 +80,7 @@ class Entity {
7580
}
7681

7782
void render(PApplet app) {
83+
app.pushStyle();
7884
app.pushMatrix();
7985

8086
// Apply local transform
@@ -88,7 +94,37 @@ class Entity {
8894
app.specular(120);
8995
app.shininess(15.0f);
9096

91-
app.fill(selected ? color(255, 180, 0) : col);
97+
if (type.equals("PointLight")) {
98+
app.resetShader();
99+
app.fill(selected ? color(255, 180, 0) : material.albedo);
100+
} else {
101+
if (p3deditor.this.pbrShader != null) {
102+
float r = ((material.albedo >> 16) & 0xFF) / 255.0f;
103+
float g = ((material.albedo >> 8) & 0xFF) / 255.0f;
104+
float b = (material.albedo & 0xFF) / 255.0f;
105+
p3deditor.this.pbrShader.set("albedo", r, g, b);
106+
p3deditor.this.pbrShader.set("metallic", material.metallic);
107+
p3deditor.this.pbrShader.set("roughness", material.roughness);
108+
109+
// v0.8.5: Pass Model Matrix for World-Space IBL
110+
PMatrix3D mm = getWorldMatrix();
111+
p3deditor.this.pbrShader.set("modelMatrix", mm);
112+
113+
// v0.8.0: Texture Maps support
114+
if (material.hasAlbedoMap) p3deditor.this.pbrShader.set("albedoMap", material.albedoMap);
115+
p3deditor.this.pbrShader.set("hasAlbedoMap", material.hasAlbedoMap);
116+
117+
if (material.hasMetallicMap) p3deditor.this.pbrShader.set("metallicMap", material.metallicMap);
118+
p3deditor.this.pbrShader.set("hasMetallicMap", material.hasMetallicMap);
119+
120+
if (material.hasRoughnessMap) p3deditor.this.pbrShader.set("roughnessMap", material.roughnessMap);
121+
p3deditor.this.pbrShader.set("hasRoughnessMap", material.hasRoughnessMap);
122+
123+
app.shader(p3deditor.this.pbrShader);
124+
}
125+
app.fill(selected ? color(255, 180, 0) : material.albedo);
126+
}
127+
92128
if (selected) {
93129
app.stroke(255, 100, 0);
94130
app.strokeWeight(1.5f);
@@ -97,10 +133,13 @@ class Entity {
97133
}
98134

99135
// Draw geometry
100-
if (type.equals("Cube")) app.box(50);
101-
else if (type.equals("Sphere")) { app.sphereDetail(30); app.sphere(30); }
102-
else if (type.equals("Plane")) { app.box(100, 1, 100); }
103-
else if (type.equals("PointLight")) {
136+
if (model != null) {
137+
app.shape(model);
138+
} else {
139+
if (type.equals("Cube")) app.box(50);
140+
else if (type.equals("Sphere")) { app.sphereDetail(30); app.sphere(30); }
141+
else if (type.equals("Plane")) { app.box(100, 1, 100); }
142+
else if (type.equals("PointLight")) {
104143
app.noStroke();
105144
app.emissive(col);
106145
app.sphere(8);
@@ -112,34 +151,31 @@ class Entity {
112151
app.line(0,0,0, cos(a)*18, sin(a)*18, 0);
113152
app.line(0,0,0, 0, cos(a)*18, sin(a)*18);
114153
}
115-
116-
// Visual Range Sphere
117-
app.noFill();
118-
app.stroke(col, 40);
119-
app.strokeWeight(0.5f);
120-
app.sphereDetail(16);
121-
app.sphere(lightRange);
122154
}
123155
}
156+
}
157+
158+
if (!type.equals("PointLight")) app.resetShader();
159+
// Visual Range Sphere
160+
if (type.equals("PointLight") && selected) { // Only draw range sphere if it's a selected PointLight
161+
app.noFill();
162+
app.stroke(col, 40);
163+
app.strokeWeight(0.5f);
164+
app.sphereDetail(16);
165+
app.sphere(lightRange);
166+
}
124167

125168
// Recursively render children
126169
for (Entity child : children) {
127170
child.render(app);
128171
}
129172

130173
app.popMatrix();
174+
app.popStyle();
131175
}
132176

133177
PVector getWorldPosition() {
134-
PVector pos = transform.position.copy();
135-
Entity p = parent;
136-
while (p != null) {
137-
// Very naive version of world pos for now (just adding offsets)
138-
// For full rotations/scales of parents, we'd need matrix multiplication
139-
// Let's use Processing's matrix math for correctness:
140-
return getWorldMatrix().mult(new PVector(0,0,0), new PVector());
141-
}
142-
return pos;
178+
return getWorldMatrix().mult(new PVector(0,0,0), new PVector());
143179
}
144180

145181
PMatrix3D getWorldMatrix() {

p3deditor/Gizmo.pde

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Gizmo {
1919
void render(PApplet app, SceneManager scene) {
2020
if (mode == 4 || scene.selectedEntities.isEmpty()) return;
2121

22+
app.pushStyle();
2223
PVector centerPos = getCenter(scene);
2324
int drawMode = mode; // Allow visual switching for all selections
2425

@@ -93,6 +94,7 @@ class Gizmo {
9394

9495
app.popMatrix();
9596
app.hint(PConstants.ENABLE_DEPTH_TEST);
97+
app.popStyle();
9698
}
9799

98100
void drawAxis(PApplet app, int drawMode) {

p3deditor/Material.pde

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* P3DE Physical Material
3+
* Stores PBR properties for the Cook-Torrance BRDF shader.
4+
*/
5+
class Material {
6+
int albedo = #FFFFFF;
7+
float metallic = 0.0;
8+
float roughness = 0.5;
9+
10+
// v0.8.0: Texture Slots
11+
PImage albedoMap = null;
12+
PImage metallicMap = null;
13+
PImage roughnessMap = null;
14+
15+
boolean hasAlbedoMap = false;
16+
boolean hasMetallicMap = false;
17+
boolean hasRoughnessMap = false;
18+
19+
void setAlbedoMap(PImage img) { this.albedoMap = img; this.hasAlbedoMap = (img != null); }
20+
void setMetallicMap(PImage img) { this.metallicMap = img; this.hasMetallicMap = (img != null); }
21+
void setRoughnessMap(PImage img) { this.roughnessMap = img; this.hasRoughnessMap = (img != null); }
22+
23+
Material() {}
24+
25+
Material(int col, float met, float rough) {
26+
this.albedo = col;
27+
this.metallic = met;
28+
this.roughness = rough;
29+
}
30+
}

p3deditor/SceneManager.pde

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class SceneManager {
1212
JSONArray sceneSnapshot = null;
1313
int snapshotNextId = 1;
1414

15+
// v0.8.0: Global IBL Environment Map
16+
PImage envMap = null;
17+
1518
void saveSnapshot() {
1619
sceneSnapshot = new JSONArray();
1720
for (int i=0; i<entities.size(); i++) {
@@ -81,6 +84,51 @@ class SceneManager {
8184
}
8285
}
8386

87+
void updateShaderLights(PShader shader) {
88+
if (shader == null) return;
89+
90+
shader.set("cameraPos", p3deditor.this.editorCamera.pos);
91+
92+
ArrayList<Entity> lights = new ArrayList<Entity>();
93+
for (Entity e : entities) {
94+
if (e.type.equals("PointLight")) lights.add(e);
95+
if (lights.size() >= 5) break;
96+
}
97+
98+
float[] lPos = new float[lights.size() * 3];
99+
float[] lCol = new float[lights.size() * 3];
100+
101+
// Use the current modelview matrix to transform light positions into view space
102+
PMatrix3D view = ((PGraphics3D)p3deditor.this.g).modelview;
103+
104+
for (int i = 0; i < lights.size(); i++) {
105+
Entity l = lights.get(i);
106+
PVector wPos = l.getWorldPosition();
107+
PVector vPos = new PVector();
108+
view.mult(wPos, vPos);
109+
110+
lPos[i*3 + 0] = vPos.x;
111+
lPos[i*3 + 1] = vPos.y;
112+
lPos[i*3 + 2] = vPos.z;
113+
114+
lCol[i*3 + 0] = p3deditor.this.red(l.col) / 255.0f * l.lightIntensity;
115+
lCol[i*3 + 1] = p3deditor.this.green(l.col) / 255.0f * l.lightIntensity;
116+
lCol[i*3 + 2] = p3deditor.this.blue(l.col) / 255.0f * l.lightIntensity;
117+
}
118+
119+
p3deditor.this.pbrShader.set("lightPositions", lPos, 3);
120+
p3deditor.this.pbrShader.set("lightColors", lCol, 3);
121+
p3deditor.this.pbrShader.set("lightCount", lights.size());
122+
123+
// v0.8.0: Environmental Lighting (IBL)
124+
if (envMap != null) {
125+
p3deditor.this.pbrShader.set("envMap", envMap);
126+
p3deditor.this.pbrShader.set("hasEnvMap", true);
127+
} else {
128+
p3deditor.this.pbrShader.set("hasEnvMap", false);
129+
}
130+
}
131+
84132
void addEntityToSceneRecursive(Entity e) {
85133
if (e.id == -1) e.id = nextEntityId++;
86134
if (!entities.contains(e)) entities.add(e);
@@ -90,6 +138,7 @@ class SceneManager {
90138
}
91139

92140
void render(PApplet app) {
141+
updateShaderLights(p3deditor.this.pbrShader);
93142
// Note: Global lighting is now handled in p3deditor.draw() to respect the 8-light limit.
94143

95144
// ONLY start rendering from root entities (hierarchical recursion handles children)

0 commit comments

Comments
 (0)