NVPathRendering doesn't work [SOLVED]

Started by pbartosz, February 09, 2013, 15:06:54

Previous topic - Next topic

pbartosz

Hi,
I'm trying to develop simple OpenGL app using NVPathRendering extenstion.
I wrote some code using LWJGL, but it doesn't seem to work.
Could you, please, tell me what's wrong in this code?
I'll be very grateful for your help. It's really important to me.

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.NVPathRendering.*;
import static org.lwjgl.opengl.EXTDirectStateAccess.*;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.ByteBuffer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIManager;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;

public class Main {
    private boolean closeRequested = false;

    private int pathObj = 42;

    public Main() {
	JFrame frame = new JFrame();
	frame.addWindowListener(new WindowAdapter() {
	    @Override
	    public void windowClosing(WindowEvent e) {
		closeRequested = true;
	    }
	});



	Canvas canvas = new Canvas();
	canvas.setSize(640, 480);
	canvas.setFocusable(true);
	canvas.setIgnoreRepaint(true);

	// frame.setLayout(new BorderLayout());
	frame.add(button, BorderLayout.NORTH);
	frame.add(canvas, BorderLayout.CENTER);
	frame.pack();
	frame.setVisible(true);

	// Display Setup
	try {
	    Display.setResizable(true);
	    Display.setParent(canvas);
	    Display.sync(60);
	    Display.create();
	} catch (LWJGLException ex) {
	    ex.printStackTrace();
	}

	System.out.println("OpenGL version: " + glGetString(GL_VERSION)); //return OpenGL 4.2

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 600, 0, 480, 1, -1);
	glMatrixMode(GL_MODELVIEW);

	String psPathString = "100 180 moveto"
		+ " 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath"
		+ " 300 300 moveto" + " 100 400 100 200 300 100 curveto"
		+ " 500 200 500 400 300 300 curveto closepath";
	ByteBuffer psPathStringBuf = ByteBuffer.allocateDirect(psPathString
		.getBytes().length);
	psPathStringBuf.put(psPathString.getBytes());

	glPathStringNV(pathObj, GL_PATH_FORMAT_PS_NV, psPathStringBuf);

	glPathParameteriNV(pathObj, GL_PATH_JOIN_STYLE_NV, GL_ROUND_NV);
	glPathParameterfNV(pathObj, GL_PATH_STROKE_WIDTH_NV, (float) 6.5);

	// Render Loop
	while (!Display.isCloseRequested() && !closeRequested) {
	    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	    pathRender(); // this do nothing!

	    // render(); //this renders correctly !

	    Display.update();
	}

	Display.destroy();
	System.exit(0); // to exit faster
    }

    private void pathRender() {
	glClearStencil(0);
	glClearColor(0, 0, 0, 0);
	glStencilMask(~0);
	glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	glMatrixLoadIdentityEXT(GL_PROJECTION);
	glMatrixLoadIdentityEXT(GL_MODELVIEW);
	glMatrixOrthoEXT(GL_MODELVIEW, 0, 640, 0, 400, -1, 1);

	glStencilFillPathNV(pathObj, GL_COUNT_UP_NV, 0x1F);

	glColor3f(0, 1, 0); // green
	glCoverFillPathNV(pathObj, GL_BOUNDING_BOX_NV);

	glStencilStrokePathNV(pathObj, 0x1, ~0);
	glColor3f(1, 1, 0); // yellow
	glCoverStrokePathNV(pathObj, GL_CONVEX_HULL_NV);
    }

    public void render() {
	glColor3f(1, 1, 1); // white render color
	glRectf(100, 100, 110, 110);
	glBegin(GL_POINTS); // point at 5 above mouse location
	glVertex2d(Mouse.getX(), Mouse.getY() + 5);
	glEnd();
    }

    public static void main(String args[]) {
	try {
	    // Set System L&F
	    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	} catch (Exception e) {
	    e.printStackTrace();
	}

	new Main();
    }
}

spasi

Without running your code, one obvious bug is not flipping the path string buffer before passing it to glPathStringNV. It should be:

String psPathString = "100 180 moveto"
		+ " 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath"
		+ " 300 300 moveto" + " 100 400 100 200 300 100 curveto"
		+ " 500 200 500 400 300 300 curveto closepath";
	ByteBuffer psPathStringBuf = ByteBuffer.allocateDirect(psPathString
		.getBytes().length);
	psPathStringBuf.put(psPathString.getBytes());
	psPathStringBuf.flip();

	glPathStringNV(pathObj, GL_PATH_FORMAT_PS_NV, psPathStringBuf);

pbartosz

Thanks! After your change something is rendering, but it's still not the shape I expect.
The below screen presents how it should look like.
I think that path isn't correctly stenciled in the stencil buffer.
SVG path looks correct:
String star = "M 300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z"; //heart

Red points in the right window represents path coordinates.
Any idea what's wrong?



import static org.lwjgl.opengl.EXTDirectStateAccess.glMatrixLoadIdentityEXT;
import static org.lwjgl.opengl.EXTDirectStateAccess.glMatrixOrthoEXT;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_KEEP;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_NOTEQUAL;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_STENCIL_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_STENCIL_TEST;
import static org.lwjgl.opengl.GL11.GL_VERSION;
import static org.lwjgl.opengl.GL11.GL_ZERO;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glClearStencil;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glGetString;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glStencilFunc;
import static org.lwjgl.opengl.GL11.glStencilMask;
import static org.lwjgl.opengl.GL11.glStencilOp;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.NVPathRendering.GL_CONVEX_HULL_NV;
import static org.lwjgl.opengl.NVPathRendering.GL_PATH_FORMAT_SVG_NV;
import static org.lwjgl.opengl.NVPathRendering.GL_PATH_JOIN_STYLE_NV;
import static org.lwjgl.opengl.NVPathRendering.GL_PATH_STROKE_WIDTH_NV;
import static org.lwjgl.opengl.NVPathRendering.GL_ROUND_NV;
import static org.lwjgl.opengl.NVPathRendering.glCoverStrokePathNV;
import static org.lwjgl.opengl.NVPathRendering.glPathParameteriNV;
import static org.lwjgl.opengl.NVPathRendering.glPathStringNV;
import static org.lwjgl.opengl.NVPathRendering.glStencilStrokePathNV;
import static org.lwjgl.opengl.NVPathRendering.*;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.ByteBuffer;

import javax.swing.JFrame;
import javax.swing.UIManager;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

public class Main {
    private boolean closeRequested = false;

    private int pathObj = 41;

    public Main() {
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
			closeRequested = true;
			}
		});
	
		Canvas canvas = new Canvas();
		canvas.setSize(500, 400);
		canvas.setFocusable(true);
		canvas.setIgnoreRepaint(true);
	
		frame.setLayout(new BorderLayout());
		frame.add(canvas, BorderLayout.CENTER);
		frame.pack();
		frame.setVisible(true);
		
		try {
			Display.setResizable(true);
			Display.setParent(canvas);
			Display.sync(60);
			Display.create();
		} catch (LWJGLException ex) {
			ex.printStackTrace();
		}
	
		System.out.println("OpenGL version: " + glGetString(GL_VERSION));
	
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, 500, 0, 400, 1, -1);
		glMatrixMode(GL_MODELVIEW);
	
		// SVG format
		String star = 
			"M 300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z"; //heart		 
		ByteBuffer starBuf =
		ByteBuffer.allocateDirect(star.getBytes().length);
		starBuf.put(star.getBytes());
		starBuf.flip();
		glPathStringNV(pathObj, GL_PATH_FORMAT_SVG_NV, starBuf);
		
		glPathParameteriNV(pathObj, GL_PATH_JOIN_STYLE_NV, GL_ROUND_NV);
		glPathParameterfNV(pathObj, GL_PATH_STROKE_WIDTH_NV, (float) 6.5);	
	
		while (!Display.isCloseRequested() && !closeRequested) {
			pathRender();
			render();
			Display.update();
		}
		Display.destroy();	
		frame.dispose();	
		//System.exit(0);
    }

    private void pathRender() {
		glClearStencil(0);
		glClearColor(0, 0, 0, 0);
		glStencilMask(~0);		
		glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		glMatrixLoadIdentityEXT(GL_PROJECTION);
		glMatrixLoadIdentityEXT(GL_MODELVIEW);
		glMatrixOrthoEXT(GL_MODELVIEW, 0, 500, 0, 400, -1, 1);

	//	glStencilFillPathNV(pathObj, GL_COUNT_UP_NV, 0x1F);	
	//	glEnable(GL_STENCIL_TEST);
	//	boolean even_odd = false;
	//	if (even_odd) {
	//	    glStencilFunc(GL_NOTEQUAL, 0, 0x1);
	//	} else {
	//	    glStencilFunc(GL_NOTEQUAL, 0, 0x1F);
	//	}
	//	glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
	//	glColor3f(1, 1, 0); // yellow
	//	glCoverFillPathNV(pathObj, GL_BOUNDING_BOX_NV);

		glStencilStrokePathNV(pathObj, 0x1, ~0);
		glColor3f(1, 1, 1); //white
		glCoverStrokePathNV(pathObj, GL_CONVEX_HULL_NV);
    }

    public void render() {
		glColor3f(1, 0, 0);
		GL11.glPointSize(5);
		glBegin(GL11.GL_POINTS); // point at 5 above mouse location	
		GL11.glVertex3f(300,300, 0);	
		GL11.glVertex3f(100,400, 0);
		GL11.glVertex3f(100,200, 0);	
		GL11.glVertex3f(300,100, 0);
		GL11.glVertex3f(500,200, 0);
		GL11.glVertex3f(500,400, 0);	
		glEnd();
    }

    public static void main(String args[]) {
	try {
	    // Set System L&F
	    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	} catch (Exception e) {
	    e.printStackTrace();
	}

	new Main();
    }
}

pbartosz

Problem solved! The problem was Stencil Buffer size set to 0 bits. Display.create(new PixelFormat(0,24,8,8)) do the trick.

I have another question.
How can I convert 2 dim array to ByteBuffer?
Below code doesn't seem to work:

byte[] pathCommands = { GL_MOVE_TO_NV, GL_LINE_TO_NV, GL_LINE_TO_NV,
		GL_LINE_TO_NV, GL_LINE_TO_NV, GL_CLOSE_PATH_NV,
		'M', 'C', 'C', 'Z' };
	ByteBuffer pathCommandsBuf = ByteBuffer.allocateDirect(pathCommands.length);
	pathCommandsBuf.put(pathCommands);
	pathCommandsBuf.flip();

        /* C++ style 2 dim array
        static const GLshort pathCoords[12][2] =
        { {100, 180}, {40, 10}, {190, 120}, {10, 120}, {160, 10},
        {300,300}, {100,400}, {100,200}, {300,100},
        {500,200}, {500,400}, {300,300} };
        */

	int pathCoords[] = { 100, 180, 40, 10, 190, 120, 10, 120, 160, 10, 300,
		300, 100, 400, 100, 200, 300, 100, 500, 200, 500, 400, 300, 300 };
	ByteBuffer pathCoordsBuf = ByteBuffer.allocateDirect(pathCoords.length * 2);
	for (int i : pathCoords) { //doesn't work!
	    pathCoordsBuf.putShort((short)pathCoords[i]);
	}	
/*
        for (int i = 0; i < pathCoords.length / 2; i++) { //renders something
	    pathCoordsBuf.putShort((short)pathCoords[i]);
	}	
*/
	pathCoordsBuf.flip();

	glPathCommandsNV(pathObj, pathCommandsBuf, GL11.GL_SHORT, pathCoordsBuf);


Edit: pathCoordsBuf.order(ByteOrder.LITTLE_ENDIAN) do the trick.

But as shown in above code, when I fill only half of pathCoordsBuf, it's rendering some shape.
In NVidia example, coords size is 24, not 48 as pathCommandsBuf.remaining() returns.
It's possible that there is a bug in
NVPathRendering.glPathCommandsNV(...)
method and there should be something like
coords.remaining() / sizeof(coordType)
as argument to native method.

Workaround is to set
pathCoordsBuf.limit(pathCoordsBuf.capacity() / 2);