Ok... I'm extremely confused. I'm attempting to create a tessellation
class... and am having issues with the Combine Callback...
The callback is defined as in GLU as:
void combine( GLdouble coords[3], void* vertex_data[4], GLfloat
weight[4], void** outData );
I've defined the delegate in C# this way:
internal delegate void GluTessCombineCallback(
[MarshalAs( UnmanagedType.LPArray, SizeConst=3 )] double[] coords,
[MarshalAs( UnmanagedType.LPArray, SizeConst=4 )] IntPtr[]
vertex_data,
[MarshalAs( UnmanagedType.LPArray, SizeConst=4 )] float[] weight,
ref IntPtr outData );
The function that registers the callback is this:
[DllImport( "glu32.dll" )]
internal static extern void gluTessCallback( IntPtr tobj, TessCallback
which,
[MarshalAs( UnmanagedType.FunctionPtr )] GluTessCombineCallback fn );
When I make a call to this registration function in the class
constructor, I get a MarshalDirectiveException thrown with a Message
of "array size control parameter must be an integral type".
What I don't understand is that I set the SizeConst in the delegate
declaration. I saw an earlier thread that had similar issues here
(unfortunately I couldn't reply to it... other issues):
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=usvzMxdgDHA.
1764%40TK2MSFTNGP09.phx.gbl&rnum=1&prev=/groups%3Fsourceid%3Dnavclient%26ie%3DUT
F-8%26oe%3DUTF-8%26q%3D%2522array%2Bsize%2Bcontrol%2Bparameter%2Bmust%2Bbe%2Ban%
2Bintegral%2Btype%2522
Is there any workaround to this problem... or am I missing something?
Thanks
The workaround is to do your own marshalling. That is, declare the
delegate as follows:
internal delegate void GluTessCombineCallback(
double* coords,
IntPtr* vertex_data,
float *weight,
out IntPtr outData);
One could also write the wrapper in Managed Extensions for C++, as follows:
==== Begin Tesselator.cpp ==============================================
#using <mscorlib.dll>
#include <windows.h>
#include <gl\Glu.h>
#include <stdio.h>
class GLUtesselator {};
typedef System::IntPtr IntPtr;
typedef System::Runtime::InteropServices::GCHandle GCHandle;
typedef System::Object Object;
typedef System::Console Console;
namespace Glu {
public __value struct Coords {
double X;
double Y;
double Z;
Coords() {}
Coords(double x, double y, double z) : X(x), Y(y), Z(z) {}
System::String *ToString() {
System::Text::StringBuilder *builder =
new System::Text::StringBuilder();
builder->Append((__wchar_t) '(');
builder->Append(X);
builder->Append((__wchar_t) ',');
builder->Append(Y);
builder->Append((__wchar_t) ',');
builder->Append(Z);
builder->Append((__wchar_t) ')');
return builder->ToString();
}
};
public __value enum PrimitiveType {
Triangles = GL_TRIANGLES,
TriangleStrip = GL_TRIANGLE_STRIP,
TriangleFan = GL_TRIANGLE_FAN
};
public __gc __interface ITesselatorResultSink {
void Begin(PrimitiveType type);
void Vertex(IntPtr vertex);
void End();
IntPtr Combine(
Coords coords,
IntPtr vertices __gc [],
float weights __gc []);
};
ITesselatorResultSink *GetResultSink(void *data) {
IntPtr ptr = IntPtr::op_Explicit(data);
GCHandle handle = GCHandle::op_Explicit(ptr);
Object *object = handle.get_Target();
return dynamic_cast<ITesselatorResultSink *>(object);
}
void __stdcall tess_beginData(GLenum type, void *polygon_data) {
GetResultSink(polygon_data)->Begin((PrimitiveType) type);
}
void __stdcall tess_vertexData(void *vertex_data, void *polygon_data) {
GetResultSink(polygon_data)->Vertex(
IntPtr::op_Explicit(vertex_data));
}
void __stdcall tess_endData(void *polygon_data) {
GetResultSink(polygon_data)->End();
}
void __stdcall tess_combineData(
GLdouble coordsArray[3],
void *vertex_data[4],
GLfloat weight[4],
void **outData,
void *polygon_data) {
Coords coords =
Coords(coordsArray[0], coordsArray[1], coordsArray[2]);
IntPtr vertices __gc [] = __gc new IntPtr[4];
float weightArray __gc [] = __gc new float __gc [4];
for (int i = 0; i < 4; i++) {
vertices[i] = IntPtr::op_Explicit(vertex_data[i]);
weightArray[i] = weight[i];
}
*outData = (void *)
GetResultSink(polygon_data)->Combine(
coords, vertices, weightArray);
}
public __sealed __gc class Tesselator : public System::IDisposable {
GLUtesselator *tesselator;
GCHandle resultSinkHandle;
public:
Tesselator() {
tesselator = gluNewTess();
gluTessCallback(
tesselator, GLU_TESS_BEGIN_DATA,
(void (__stdcall *)(void)) &tess_beginData);
gluTessCallback(
tesselator, GLU_TESS_VERTEX_DATA,
(void (__stdcall *)(void)) &tess_vertexData);
gluTessCallback(
tesselator, GLU_TESS_END_DATA,
(void (__stdcall *)(void)) &tess_endData);
gluTessCallback(
tesselator, GLU_TESS_COMBINE_DATA,
(void (__stdcall *)(void)) &tess_combineData);
}
void Dispose() {
gluDeleteTess(tesselator);
}
void BeginPolygon(ITesselatorResultSink *resultSink) {
resultSinkHandle = GCHandle::Alloc(resultSink);
IntPtr ptr = (IntPtr) resultSinkHandle;
void *data = (void *) ptr;
gluTessBeginPolygon(tesselator, data);
}
void BeginContour() {
gluTessBeginContour(tesselator);
}
void Vertex(Coords coords, IntPtr vertex) {
double cs[3] = {coords.X, coords.Y, coords.Z};
gluTessVertex(tesselator, cs, (void *) vertex);
}
void EndContour() {
gluTessEndContour(tesselator);
}
void EndPolygon() {
gluTessEndPolygon(tesselator);
resultSinkHandle.Free();
}
};
}
==== End Tesselator.cpp ================================================
Clients of this wrapper need to represent vertices as IntPtrs; however,
it is easy to write a wrapper around this wrapper that allows vertices
to be represented as objects:
==== Begin TesselatorEx.cs =============================================
using System;
using System.Collections;
using Glu;
namespace GluEx {
public interface ITesselatorExResultSink {
void Begin(PrimitiveType type);
void Vertex(object vertex);
void End();
object Combine(Coords coords, object[] vertices, float[] weights);
}
public class TesselatorEx : IDisposable {
class ResultSink : ITesselatorResultSink {
TesselatorEx outer;
public ResultSink(TesselatorEx outer) {
this.outer = outer;
}
public void Begin(PrimitiveType type) {
outer.exResultSink.Begin(type);
}
public void Vertex(IntPtr ptr) {
object vertex = outer.GetVertexFromIntPtr(ptr);
outer.exResultSink.Vertex(vertex);
}
public void End() {
outer.exResultSink.End();
}
public IntPtr Combine(
Coords coords, IntPtr[] ptrs, float[] weight) {
object[] vertices = new object[ptrs.Length];
for (int i = 0; i < ptrs.Length; i++) {
vertices[i] = outer.GetVertexFromIntPtr(ptrs[i]);
}
object result =
outer.exResultSink.Combine(coords, vertices, weight);
return outer.GetIntPtrFromVertex(result);
}
}
Tesselator tesselator = new Tesselator();
ResultSink resultSink;
ArrayList list;
Hashtable table;
ITesselatorExResultSink exResultSink;
IntPtr GetIntPtrFromVertex(object vertex) {
object index = table[vertex];
int i;
if (index == null) {
i = list.Count;
list.Add(vertex);
table[vertex] = i;
} else {
i = (int) index;
}
return (IntPtr) (i + 1);
}
object GetVertexFromIntPtr(IntPtr ptr) {
return list[(int) ptr - 1];
}
public TesselatorEx() {
resultSink = new ResultSink(this);
}
public void BeginPolygon(ITesselatorExResultSink exResultSink) {
this.exResultSink = exResultSink;
list = new ArrayList();
table = new Hashtable();
tesselator.BeginPolygon(resultSink);
}
public void BeginContour() {
tesselator.BeginContour();
}
public void Vertex(Coords coords, object vertex) {
tesselator.Vertex(coords, GetIntPtrFromVertex(vertex));
}
public void EndContour() {
tesselator.EndContour();
}
public void EndPolygon() {
tesselator.EndPolygon();
table = null;
list = null;
exResultSink = null;
}
public void Dispose() {
tesselator.Dispose();
}
}
}
==== End TesselatorEx.cs ===============================================
This code can be tested as follows:
==== Begin Test.cs =====================================================
using Glu;
using GluEx;
using System;
using Color = System.Drawing.Color;
class Vertex {
static int counter;
public int id = ++counter;
public Coords coords;
public Color color;
public Vertex(Coords coords, Color color) {
this.coords = coords;
this.color = color;
}
}
class MyResultSink : ITesselatorExResultSink {
public void Begin(PrimitiveType type) {
Console.WriteLine(
"MyResultSink::Begin(type == {0}) called", type);
}
public void Vertex(object vertex) {
Console.WriteLine(
"MyResultSink::Vertex(vertex == {0}) called",
((Vertex) vertex).id);
}
public void End() {
Console.WriteLine("MyResultSink::End() called");
}
public object Combine(
Coords coords, object[] vertices, float[] weights) {
float alpha = 0;
float red = 0;
float green = 0;
float blue = 0;
for (int i = 0; i < vertices.Length; i++) {
Vertex vertex = (Vertex) vertices[i];
float weight = weights[i];
alpha += weight * vertex.color.A;
red += weight * vertex.color.R;
green += weight * vertex.color.G;
blue += weight * vertex.color.B;
}
Vertex result =
new Vertex(
coords,
Color.FromArgb(
(int) alpha, (int) red, (int) green, (int) blue));
Console.WriteLine(
"MyResultSink::Combine(" +
"coords == {0}, " +
"vertices == {{{1},{2},{3},{4}}}, " +
"weights == {{{5},{6},{7},{8}}}" +
") called; result == {9}",
coords.ToString(),
((Vertex) vertices[0]).id,
((Vertex) vertices[1]).id,
((Vertex) vertices[2]).id,
((Vertex) vertices[3]).id,
weights[0], weights[1], weights[2], weights[3],
result.id
);
return result;
}
}
class Test {
static void Vertex(
TesselatorEx tesselator, Coords coords, Color color) {
tesselator.Vertex(coords, new Vertex(coords, color));
}
static void Main() {
TesselatorEx tesselator = new TesselatorEx();
MyResultSink sink = new MyResultSink();
tesselator.BeginPolygon(sink);
tesselator.BeginContour();
Vertex(tesselator, new Coords(0, 0, 0), Color.Black);
Vertex(tesselator, new Coords(2, 0, 0), Color.Red);
Vertex(tesselator, new Coords(2, 2, 0), Color.Green);
Vertex(tesselator, new Coords(0, 2, 0), Color.Blue);
tesselator.EndContour();
tesselator.BeginContour();
Vertex(tesselator, new Coords(1, 1, 0), Color.White);
Vertex(tesselator, new Coords(3, 1, 0), Color.Yellow);
Vertex(tesselator, new Coords(3, 3, 0), Color.Cyan);
Vertex(tesselator, new Coords(1, 3, 0), Color.Magenta);
tesselator.EndContour();
tesselator.EndPolygon();
tesselator.Dispose();
}
}
==== End Test.cs =======================================================
The only thing to watch out for is the Mixed DLL Loading Problem
<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/ht
ml/vcconmixeddllloadingproblem.asp>.
Greetings
Bart Jacobs