Skip to content

Commit 665a66e

Browse files
committed
implemented #342
1 parent 3cd2620 commit 665a66e

17 files changed

Lines changed: 413 additions & 161 deletions

File tree

src/main/java/com/marginallyclever/ro3/PanelHelper.java

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import javax.swing.text.NumberFormatter;
1313
import java.awt.*;
1414
import java.awt.image.BufferedImage;
15+
import java.util.Objects;
16+
import java.util.function.BiConsumer;
1517
import java.util.function.Consumer;
1618
import java.util.function.Supplier;
1719

@@ -270,12 +272,109 @@ public static void addTextureField(JPanel panel,
270272
setTextureButtonLabel(button,texture);
271273
}
272274

275+
/**
276+
* <p>A convenience method to add a control for selecting a color source. Currently that means a toggle between
277+
* a color selector and a texture selector.</p>
278+
* @param panel the panel to add to
279+
* @param label the label for the field
280+
* @param isTextureSupplier supplier to check if the current mode is texture
281+
* @param colorSupplier supplier to get the current color
282+
* @param textureSupplier supplier to get the current texture
283+
* @param updateConsumer consumer to accept the mode and the value (Color or TextureWithMetadata)
284+
* @param gbc the {@link GridBagConstraints} to use
285+
*/
286+
public static void addColorSourceControl(JPanel panel,
287+
String label,
288+
Supplier<Boolean> isTextureSupplier,
289+
Supplier<Color> colorSupplier,
290+
Supplier<TextureWithMetadata> textureSupplier,
291+
BiConsumer<Boolean, Object> updateConsumer,
292+
GridBagConstraints gbc) {
293+
JPanel container = new JPanel(new BorderLayout());
294+
295+
JButton modeButton = new JButton();
296+
int fontHeight = modeButton.getFontMetrics(modeButton.getFont()).getHeight();
297+
var insets = modeButton.getBorder().getBorderInsets(modeButton);
298+
var height = fontHeight + insets.top + insets.bottom;
299+
300+
modeButton.setMinimumSize(new Dimension(0, height));
301+
modeButton.setPreferredSize(new Dimension(24, height));
302+
modeButton.setMargin(new Insets(0, 0, 0, 0));
303+
304+
JButton valueButton = new JButton();
305+
fontHeight = valueButton.getFontMetrics(valueButton.getFont()).getHeight();
306+
valueButton.setMinimumSize(new Dimension(0, height));
307+
valueButton.setPreferredSize(new Dimension(valueButton.getPreferredSize().width, fontHeight + 8));
308+
309+
Runnable updateUI = () -> {
310+
boolean isTexture = isTextureSupplier.get();
311+
if (isTexture) {
312+
modeButton.setIcon(new ImageIcon(Objects.requireNonNull(PanelHelper.class.getResource("/com/marginallyclever/ro3/node/nodes/icons8-texture-16.png"))));
313+
modeButton.setToolTipText("Switch to Color");
314+
modeButton.setText("");
315+
setTextureButtonLabel(valueButton, textureSupplier.get());
316+
valueButton.setBackground(null);
317+
} else {
318+
modeButton.setIcon(null); // TODO: find a color icon? maybe just a colored square.
319+
modeButton.setText("C");
320+
modeButton.setToolTipText("Switch to Texture");
321+
valueButton.setIcon(null);
322+
valueButton.setText("");
323+
valueButton.removeAll();
324+
valueButton.setLayout(new BorderLayout());
325+
valueButton.setToolTipText(null);
326+
valueButton.setBackground(colorSupplier.get());
327+
valueButton.revalidate();
328+
valueButton.repaint();
329+
}
330+
};
331+
332+
modeButton.addActionListener(e -> {
333+
boolean wasTexture = isTextureSupplier.get();
334+
if (wasTexture) {
335+
updateConsumer.accept(false, colorSupplier.get());
336+
} else {
337+
updateConsumer.accept(true, textureSupplier.get());
338+
}
339+
updateUI.run();
340+
});
341+
342+
valueButton.addActionListener(e -> {
343+
boolean isTexture = isTextureSupplier.get();
344+
if (isTexture) {
345+
var textureChooserDialog = new TextureChooserDialog();
346+
textureChooserDialog.setSelectedItem(textureSupplier.get());
347+
int result = textureChooserDialog.run(panel);
348+
if (result == JFileChooser.APPROVE_OPTION) {
349+
var newTexture = textureChooserDialog.getSelectedItem();
350+
updateConsumer.accept(true, newTexture);
351+
updateUI.run();
352+
}
353+
} else {
354+
Color color = JColorChooser.showDialog(panel, label, colorSupplier.get());
355+
if (color != null) {
356+
updateConsumer.accept(false, color);
357+
updateUI.run();
358+
}
359+
}
360+
});
361+
362+
container.add(modeButton, BorderLayout.WEST);
363+
container.add(valueButton, BorderLayout.CENTER);
364+
365+
updateUI.run();
366+
PanelHelper.addLabelAndComponent(panel, label, container, gbc);
367+
}
368+
273369
private static void setTextureButtonLabel(JButton button,TextureWithMetadata texture) {
370+
button.removeAll();
274371
if(texture==null) {
372+
button.setLayout(new BorderLayout());
275373
button.setText("...");
276374
button.setToolTipText(null);
277375
return;
278376
}
377+
button.setText("");
279378
// truncate name if it is too long.
280379
var name = texture.getSource();
281380
button.setToolTipText(name);
@@ -284,10 +383,12 @@ private static void setTextureButtonLabel(JButton button,TextureWithMetadata tex
284383
} else if(name.contains("/")) {
285384
name = name.substring(name.lastIndexOf('/')+1);
286385
}
287-
button.removeAll();
288386
button.setLayout(new BoxLayout(button,BoxLayout.X_AXIS));
289-
button.add(new JLabel(new ImageIcon(scaleImage(texture.getImage(),button.getFont().getSize()))));
387+
var height = button.getFontMetrics(button.getFont()).getHeight();
388+
button.add(new JLabel(new ImageIcon(scaleImage(texture.getImage(),height))));
290389
button.add(new JLabel(" "+name));
390+
button.revalidate();
391+
button.repaint();
291392
}
292393

293394
private static BufferedImage scaleImage(BufferedImage sourceImage,int size) {

src/main/java/com/marginallyclever/ro3/apps/pathtracer/PathTracer.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
*/
3333
public class PathTracer {
3434
public static final int CHANNEL_VIEWPORT_U = 0;
35-
public static final int CHANNEL_VIEWPORT_V = 0;
36-
public static final int CHANNEL_HEMISPHERE_U = 0;
37-
public static final int CHANNEL_HEMISPHERE_V = 0;
35+
public static final int CHANNEL_VIEWPORT_V = 1;
36+
public static final int CHANNEL_HEMISPHERE_U = 2;
37+
public static final int CHANNEL_HEMISPHERE_V = 3;
3838
public static final int CHANNEL_RUSSIAN_ROULETTE = 4;
3939
public static final int CHANNEL_LIGHT_SAMPLING = 5;
4040
public static final int CHANNEL_BSDF_SAMPLING = 6;
@@ -240,7 +240,7 @@ private void probabilisticLightSampling(double p, Ray ray, Hit hit, Material mat
240240
double wBSDF = misWeight(pb,pl);
241241

242242
// contribution
243-
ColorDouble contribution = new ColorDouble(mat2.getEmittedLight());
243+
ColorDouble contribution = new ColorDouble(mat2.getEmittedLight(lightHit));
244244
contribution.multiply(brdf);
245245
contribution.scale( 1.0 / (pdfLight*p));
246246
contribution.multiply(throughput);
@@ -279,7 +279,7 @@ private boolean russianRouletteTermination(ColorDouble throughput,RayXY pixel) {
279279
private void handleEmissiveHit(Ray ray, Hit hit, Ray prevRay, Hit prevHit, ColorDouble throughput, ColorDouble radiance, Material mat, ScatterRecord prevScatter) {
280280
if(prevHit == null) {
281281
// First bounce: no MIS, just add light directly
282-
ColorDouble emittedLight = mat.getEmittedLight();
282+
ColorDouble emittedLight = mat.getEmittedLight(hit);
283283
emittedLight.multiply(throughput);
284284
emittedLight.clamp(0, maxContribution);
285285
radiance.add(emittedLight);
@@ -301,7 +301,7 @@ private void handleEmissiveHit(Ray ray, Hit hit, Ray prevRay, Hit prevHit, Color
301301
lightDir.normalize();
302302
double cosThetaLight = Math.max(0.0, -hit.normal().dot(lightDir));
303303

304-
ColorDouble emittedLight = mat.getEmittedLight();
304+
ColorDouble emittedLight = mat.getEmittedLight(hit);
305305

306306
// if the previous bounce was specular we can only have come from the BSDF sampling
307307
// so we don't need to do MIS.
@@ -446,8 +446,8 @@ private RayXY fireOneRayWithTracingOn(int x, int y,int traceDepth) {
446446
*/
447447
private Ray getRayForXY(int x,int y) {
448448
return activeCamera.getRayThroughPoint(
449-
(2.0 * x / canvasWidth) - 1.0,
450-
1.0 - (2.0 * y / canvasHeight),
449+
(2.0 * (x + 0.5) / canvasWidth) - 1.0,
450+
1.0 - (2.0 * (y + 0.5) / canvasHeight),
451451
canvasWidth, canvasHeight);
452452
}
453453

src/main/java/com/marginallyclever/ro3/apps/viewport/TextureLayerIndex.java renamed to src/main/java/com/marginallyclever/ro3/apps/viewport/MaterialLayers.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
* Associates layer enums, names, and indexes for easy global reference.
55
* Shaders should use these indexes to access the correct texture layers.
66
*/
7-
public enum TextureLayerIndex {
7+
public enum MaterialLayers {
88
ALBEDO("Albedo",0), // aka the diffuse layer
99
NORMAL("Normal",1),
1010
METALLIC("Metallic",2), // aka reflectance
1111
ROUGHNESS("Roughness",3), // aka the bump map
12-
AO("AO",4); // Ambient occlusion (baked in shadowing)
12+
AO("AO",4), // Ambient occlusion (baked in shadowing)
13+
EMISSIVE("Emissive", 5);
1314

1415
private final String name;
1516
private final int index;
1617

17-
TextureLayerIndex(String name, int index) {
18+
MaterialLayers(String name, int index) {
1819
this.name = name;
1920
this.index = index;
2021
}

src/main/java/com/marginallyclever/ro3/apps/viewport/Viewport.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ public Ray getRayThroughPointUntransformed(Camera camera,double normalizedX,doub
578578
normalizedX*t*getAspectRatio(),
579579
normalizedY*t,
580580
-1);
581+
direction.normalize();
581582
var origin = new Point3d();
582583

583584
return new Ray(origin,direction);
@@ -598,8 +599,8 @@ public Vector3d getCursorAsNormalized() {
598599
*/
599600
public Vector3d getCursorAsNormalized(double x,double y) {
600601
return new Vector3d(
601-
(2.0*x/canvasWidth) - 1.0,
602-
1.0 - (2.0*y/canvasHeight),
602+
(2.0*(x + 0.5)/canvasWidth) - 1.0,
603+
1.0 - (2.0*(y + 0.5)/canvasHeight),
603604
0);
604605
}
605606

src/main/java/com/marginallyclever/ro3/apps/viewport/renderpass/DrawBackground.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,17 @@
77
import com.marginallyclever.convenience.helpers.MatrixHelper;
88
import com.marginallyclever.convenience.helpers.ResourceHelper;
99
import com.marginallyclever.ro3.Registry;
10-
import com.marginallyclever.ro3.apps.viewport.TextureLayerIndex;
1110
import com.marginallyclever.ro3.node.nodes.Material;
1211
import com.marginallyclever.ro3.shader.ShaderProgram;
1312
import com.marginallyclever.ro3.apps.viewport.Viewport;
1413
import com.marginallyclever.ro3.factories.Lifetime;
1514
import com.marginallyclever.ro3.node.nodes.environment.Environment;
1615
import com.marginallyclever.ro3.node.nodes.pose.poses.Camera;
17-
import com.marginallyclever.ro3.texture.TextureWithMetadata;
1816
import org.slf4j.Logger;
1917
import org.slf4j.LoggerFactory;
2018

2119
import javax.vecmath.Matrix4d;
2220
import javax.vecmath.Vector3d;
23-
import java.awt.*;
2421

2522
/**
2623
* <p>Draw the background. This may be a skybox or a solid color.</p>

src/main/java/com/marginallyclever/ro3/apps/viewport/renderpass/DrawMeshes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import com.marginallyclever.convenience.helpers.OpenGLHelper;
77
import com.marginallyclever.convenience.helpers.ResourceHelper;
88
import com.marginallyclever.ro3.Registry;
9-
import com.marginallyclever.ro3.apps.viewport.TextureLayerIndex;
9+
import com.marginallyclever.ro3.apps.viewport.MaterialLayers;
1010
import com.marginallyclever.ro3.mesh.proceduralmesh.GenerativeMesh;
1111
import com.marginallyclever.ro3.shader.ShaderProgram;
1212
import com.marginallyclever.ro3.apps.viewport.Viewport;
@@ -41,7 +41,7 @@ public class DrawMeshes extends AbstractRenderPass {
4141
private final Mesh shadowQuad = new GenerativeMesh();
4242
private final int [] shadowFBO = new int[1]; // Frame Buffer Object
4343
private final int [] shadowTexture = new int[1]; // texture for the FBO
44-
private final int shadowMapUnit = TextureLayerIndex.values().length+1;
44+
private final int shadowMapUnit = MaterialLayers.values().length+1;
4545
public static final int SHADOW_WIDTH = 4096;
4646
public static final int SHADOW_HEIGHT = 4096;
4747

src/main/java/com/marginallyclever/ro3/factories/Factory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,18 @@ public void addItemListener(ListListener<?> listener) {
2929
public void removeItemListener(ListListener<?> listener) {
3030
listenerList.remove(ListListener.class, listener);
3131
}
32+
33+
@SuppressWarnings("unchecked") // because getListeners is not generic
34+
protected <T> void fireItemAdded(T item) {
35+
for (ListListener<T> listener : listenerList.getListeners(ListListener.class)) {
36+
listener.itemAdded(this, item);
37+
}
38+
}
39+
40+
@SuppressWarnings("unchecked") // because getListeners is not generic
41+
protected <T> void fireItemRemoved(T item) {
42+
for (ListListener<T> listener : listenerList.getListeners(ListListener.class)) {
43+
listener.itemRemoved(this, item);
44+
}
45+
}
3246
}

0 commit comments

Comments
 (0)