I want to get nested stencils to work in OpenGL.
I call them masks from now on.
So the stencil buffer is cleared to all 0's.
I make my first mask, the grey area.
Now that have to be all 1's in the stencil buffer, and normal drawing is not allowed to happen outside that mask.
Now I create a second mask which is a child of the first one:
It would only update the stencil buffer if the area is inside the area of the previous mask:
Same if we would make another child mask, we would only increment if it was inside the parent mask.
Now if we would end the last mask (the green one), we can decrement the values where they where equal to the mask depth.
Ok that is the idea, but I have been stuck on getting this to work for weeks (not full time...).
Either masking does not work, or it does not work as expected. Here I provide code that makes the most sense to me.
I use processing, which can be downloaded here https://processing.org/ (download and run, simple as that).
PGL pgl;
Rect current_rect;
void setup() {
size(600, 600, P3D);
noStroke();
// will have a depth of 0, equal to the stencil buffer after a clear
current_rect = make_rect(null, 0, 0, width, height);
}
class Rect {
float x;
float y;
float w;
float h;
ArrayList<Rect> children = new ArrayList<Rect>();
Rect parent;
int mask_depth;
}
Rect make_rect(Rect parent, float x, float y, float w, float h) {
Rect r = new Rect();
r.x = x;
r.y = y;
r.w = w;
r.h = h;
if (parent != null) {
r.parent = parent;
r.mask_depth = parent.mask_depth + 1;
parent.children.add(r);
}
return r;
}
void draw() {
if (frameCount == 2) noLoop();
println();
pgl = beginPGL();
pgl.enable(PGL.STENCIL_TEST);
pgl.clear(PGL.STENCIL_BUFFER_BIT);
background(150, 80, 70);
begin_mask(100, 100, 400, 400);
fill(150);
rect(100, 100, 400, 400);
// SHOULD NOT FALL OUTSIDE OF THE MASK!
debug_text(" A ");
// disabled for now cause the above begin_mask
// goes already wrong
if (false) {
begin_mask(50, 150, 500, 100); // B
fill(100);
rect(50, 150, 500, 100);
debug_text(" B ");
if (true) {
begin_mask(200, 50, 100, 500); // C
fill(50);
rect(200, 50, 100, 500);
debug_text(" C ");
end_mask(); // C
}
end_mask(); // B
// why is the one from above (C) on this one?
// disable this one to see what I mean
if (true) {
begin_mask(50, 350, 500, 100); // D
fill(75);
rect(50, 350, 500, 100);
fill(255);
debug_text(" D ");
end_mask(); // D
}
}
end_mask(); // A
flush();
endPGL();
}
void begin_mask(float x, float y, float w, float h) {
current_rect = make_rect(current_rect, x, y, w, h);
flush();
pgl.colorMask(false, false, false, false);
pgl.depthMask(false);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.INCR);
println("increment stencil when stencil is equal to: "+current_rect.parent.mask_depth);
pgl.stencilFunc(PGL.EQUAL, current_rect.parent.mask_depth, 0xFF);
// write to stencil buffer
noStroke();
fill(0);
rect(x, y, w, h);
flush();
enable_normal_draw_mode();
}
void enable_normal_draw_mode() {
pgl.stencilMask(0x00);
println("normal write when stencil depth is: "+(current_rect.mask_depth));
pgl.stencilFunc(PGL.GEQUAL, current_rect.mask_depth, 0xff);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.KEEP);
pgl.colorMask(true, true, true, true);
pgl.depthMask(true);
flush();
println("enable_normal_draw_mode "+current_rect.mask_depth);
}
void end_mask() {
// decrement stencil mask as if we never existed
pgl.stencilMask(0xff);
pgl.stencilFunc(PGL.GEQUAL, current_rect.mask_depth, 0xff);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.DECR);
pgl.colorMask(false, false, false, false);
pgl.depthMask(false);
noStroke();
fill(0);
rect(current_rect.x, current_rect.y, current_rect.w, current_rect.h);
flush();
current_rect = current_rect.parent;
enable_normal_draw_mode();
}
void debug_text(String s) {
fill(255);
text(xxx(s), 0, 0);
}
String xxx(String to_repeat) {
String result = "";
String line = "";
for (int x = 0; x < 50; x++) {
line += to_repeat;
}
for (int y = 0; y < 50; y++) {
result += line + "\n";
}
return result;
}
Completing a graphics program in Java, I'm trying to animate rain falling using a timer. Right now I am testing my code with a big blue rectangle so I can see where it's going but the animation isn't working for me. I'm very new to Java graphics so I could be making mistakes that just aren't clear to me.
When I try to repaint the square to move, and the paint function is called the whole screen blinks, this may be because I was using recursive functions to draw fractal trees, but I'm not sure. Is there a way to keep everything I have drawn from being repainted and just call repaint on the rain? Any guidance or tips would be appreciated.
import java.awt.*;
import javax.swing.*;
import java.lang.Math;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class FractalTree extends JFrame {
private int frameWidth = 1440;
private int frameHeight = 850;
private int rainX = 0;
private int rainY = 0;
public FractalTree()
{
setBounds(1000, 1000, frameWidth, frameHeight ); //graphics window size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ActionListener listener = new TimerListener();
final int DELAY = 1500;
Timer t = new Timer(DELAY, listener);
t.start();
setResizable(false);
}
public void setRain(int newRainX, int newRainY)
{
rainX = newRainX;
rainY = newRainY;
}
public void setSkyGround(Graphics g)
{
Color sky = new Color(180, 225, 255);
g.setColor(sky);
g.fillRect(0, 0, frameWidth, 550);
Color sun = new Color(225, 225, 150);
g.setColor(sun);
g.fillOval(1380, -40, 100, 100);
g.drawLine(frameWidth, 0, 1350, 550);
g.drawLine(frameWidth, 0, 1450, 550);
g.drawLine(1350, 550, 1450, 550);
int xpoints[] = {frameWidth, 1450, 1350};
int ypoints[] = {0, 550, 550};
int npoints = 3;
g.fillPolygon(xpoints, ypoints, npoints);
g.drawLine(frameWidth, 0, 1080, 550);
g.drawLine(frameWidth, 0, 880, 550);
g.drawLine(880, 550, 1080, 550);
int xpoints2[] = {frameWidth, 1080, 880};
int ypoints2[] = {0, 550, 550};
int npoints2 = 3;
g.fillPolygon(xpoints2, ypoints2, npoints2);
g.drawLine(frameWidth, 0, 480, 550);
g.drawLine(frameWidth, 0, 280, 550);
g.drawLine(480, 550, 280, 550);
int xpoints3[] = {frameWidth, 480, 280};
int ypoints3[] = {0, 550, 550};
int npoints3 = 3;
g.fillPolygon(xpoints3, ypoints3, npoints3);
g.drawLine(frameWidth, 0, 0, 430);
g.drawLine(frameWidth, 0, 0, 300);
g.drawLine(0, 430, 0, 300);
int xpoints4[] = {frameWidth, 0, 0};
int ypoints4[] = {0, 430, 300};
int npoints4 = 3;
g.fillPolygon(xpoints4, ypoints4, npoints4);
g.drawLine(frameWidth, 0, 0, 100);
g.drawLine(frameWidth, 0, 0, 0);
g.drawLine(0, 100, 0, 0);
int xpoints5[] = {frameWidth, 0, 0};
int ypoints5[] = {0, 0, 100};
int npoints5 = 3;
g.fillPolygon(xpoints5, ypoints5, npoints5);
Color grassBackground = new Color(150, 255, 170);
g.setColor(grassBackground);
g.fillRect(0, 550, frameWidth, frameHeight);
}
public void drawTree(Graphics g, int x1, int y1, double angle, int depth, int red, int green, int blue)
{
if (depth == 0)
{
Color doodle = new Color(red, green, blue);
g.setColor(doodle);
g.fillOval(x1, y1, 10, 10);
}
else
{
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(depth));
Color brown = new Color(100, 25, 0);
g.setColor(brown);
int x2 = x1 + (int) (Math.cos(Math.toRadians(angle)) * depth * 10);
int y2 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10);
g.drawLine(x1, y1, x2, y2);
drawTree(g, x2, y2, angle - 40, depth - 1, red, green, blue);
drawTree(g, x2, y2, angle + 20, depth - 1, red, green, blue);
}
}
public void realFlowers(Graphics g, int x, int y, int lenWid, int petals)
{
//calculates the increment
double inc = (2*Math.PI/petals);
g.setColor(Color.YELLOW);
//draws petals
for(int i = 0; i < petals; i++){
//keeps spacing consistent depandng on number of petals
double value = i * inc;
//draws petals with calculated spacing relative to number of petals
g.fillOval((int)((lenWid)*Math.cos(value)+x-lenWid/4),(int)((lenWid)*Math.sin(value)+y-lenWid/4), lenWid + lenWid/2, lenWid + lenWid/2);
}
//draws middle flower bud;
g.setColor(Color.ORANGE);
g.fillOval(x - lenWid/4, y - lenWid/4, lenWid + lenWid/2 , lenWid + lenWid/2);
}
public void drawGrass(Graphics g, int width, int height, int interval, int red, int green, int blue)
{
height = frameHeight - height;
Color grass = new Color(red, green, blue);
for(int i = 0; i < width; i= i + interval)
{
for(int j = frameHeight; j > height; j = j - interval)
{
g.setColor(grass);
g.fillRect(i, j, 3, 5);
}
}
}
public void rainDrops(Graphics g, int x, int y, int w, int h)
{
setRain(x, y);
Color rain = new Color(0, 76, 153);
g.setColor(rain);
g.fillRect(x, y, w, h);
}
public void moveRainBy(int dx, int dy)
{
rainX = rainX + dx;
rainY = rainY + dy;
repaint();
}
class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
moveRainBy(1, 1);
}
}
public void paint(Graphics g) {
setSkyGround(g);
drawGrass(g, 1440, 315, 5, 0, 255, 0);
drawGrass(g, 1430, 310, 10, 0, 204, 0);
drawTree(g, 1085, 730, -90, 10, 255, 102, 102);
drawTree(g, 250, 600, -90, 8, 255, 255, 255);
drawTree(g, 1110, 740, -90, 4, 255, 102, 102);
drawTree(g, 1060, 745, -90, 2, 255, 102, 102);
realFlowers(g, 700,700, 8, 8);
rainDrops(g, 200, 200, 30, 30);
}
public static void main(String[] args) {
new FractalTree().setVisible(true);
}
}
When I try to repaint the square to move, and the paint function is called the whole screen blinks
This is because you've override paint of the a top level container (JFrame) which is not double buffered.
As a general recommendation, you should be basing your core functionality around a JPanel and override it's paintComponent method. Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
You might like to also have a look at How to get the EXACT middle of a screen, even when re-sized and How can I set in the midst? for more details why it's not recommended to extend directly from JFrame and try to paint to it.
Is there a way to keep everything I have drawn from being repainted and just call repaint on the rain?
Painting is destructive, that is, each time paint/paintComponent is called, you are expected to repaint the entire state of the component from scratch.
You could use a buffering technique, using something like BufferedImage to paint your state to and simply have the paint methods draw the image, but that would depend on how complex a solution you want. If you were to use buffering technique, I would consider which elements are "static" and which elements are "dynamic". Painting those static elements to the buffer and then, when paint is called, painting the dynamic elements over the top the buffer
I'm trying to spawn multiple versions of the same sprite. Each spawns with a random speed (using random numbers between minus and positive speeds to determine random direction) and I have created 2 for loops to load an arraylist with new sprites. However, it seems to spawn 5 sprites, but then it seems like they're deleting and creating 5 more over and over. Below are the methods used:
public void update(){
spritesArrayList.clear();
if (gameOver != true)
{
sprite.update();
}
for (int i = 0; i < count; i++)
{
sprite = new Sprite(this);
spritesArrayList.add(sprite);
}
}
/**
* To draw the game to the screen
* This is called from Thread, so synchronisation can be done
*/
public void doDraw(Canvas canvas) {
Paint textPaint = new Paint();
canvas.drawBitmap(mBackgroundImage, 0, 0, null);
//Draw all the objects on the canvas
canvas.drawText("The Game ",5,25, paint);
canvas.drawText("Score: " + hitCount, 5, 50, paint);
canvas.drawText("Time: " +displayTime, 5, 75, paint);
GetArrayListSize();
//Loop for sprite creation
for (int i = 0; i < arraySize; i++)
{
Sprite sprite = spritesArrayList.get(i);
sprite.draw(canvas);
}
if (gameOver == true)
{
canvas.drawText("Final Score: "+finalScore, 5,100, paint);
int width = this.getMeasuredWidth()/2;
int height = this.getMeasuredHeight()/2;
textPaint.setTextAlign(Align.CENTER);
canvas.drawText("GAME OVER - PRESS BACK BUTTON TO RETURN", width, height, textPaint);
}
}
I am trying to figure out why is my Array not showing any lines, I am trying to make it draw 10 different lines, now println of x and z does show 10 coordinates but they are identical and it seems as if they are being passed down to the object line itself but not drawn. It seems quite simple but I know I've made a mistake somewhere but not sure where.
lines[] mylinesArray = new lines[10];
void setup() {
size(1028, 768, P3D);
translate(width/2, height/2, 0);
rotateX(radians(180));
float prp = -2000;
float vrp = 50;
float z = random(prp, 2000);
float x = random(vrp, 500);
for (int i = 0; i<mylinesArray.length; i++) {
mylinesArray[i] = new lines(z, 0, x);
println(z);
println(x);
}
}
void draw() {
background(255);
for (int i=0; i<mylinesArray.length; i++) {
// println(i);
mylinesArray[i].drawlines();
//println("still doing it");
}
}
class lines {
color fillColor;
color strokeColor;
PVector dot1;
PVector dot2;
lines(float xpos, float ypos, float zpos) {
dot1 = new PVector(500+xpos, 200, 700+zpos);
dot2 = new PVector(500+xpos, -400, 700+zpos);
fillColor = strokeColor = color(random(255), random(255), random(255));
}
void drawlines() {
pushMatrix();
line (dot1.x, dot1.y, dot1.z, dot2.x, dot2.y, dot2.z);
stroke(0);
// println("new");
popMatrix();
}
void whatever() {
// println("please");
}
}