What do you think about 'marker interface pattern'?

Started by
2 comments, last by Aybe One 1 month ago

So, I'm writing a new engine for an old game in Unity and I am also supporting subsequent episodes of it.

After implementing the 3D formats among episodes (they are different), I ended up with 30+ types of polygons.
As you can see below, they all fit on some category, flat-shaded, Gouraud-shaded, triangle, quad.

Since I needed to make this all abstract, this marker interface pattern (which I never heard of before) came in naturally.
If you don't know what it is, basically, it's empty interfaces to allow tagging families of objects.

Here's a shortened version:

interface IPolygon // base interface
{
	vertices // array, 3 or 4 items
	normals // array, 1, 3, or 4 items
	texture // integer, -1 if not textured
	texture UVs // array, 3 or 4 items
}

interface IPolygonF3 {} // flat-shaded triangle
interface IPolygonF4 {} // flat-shaded quad
interface IPolygonFT3 {} // flat-shaded textured triangle
interface IPolygonFT4 {} // flat-shaded textured quad
interface IPolygonG3 {} // gouraud-shaded triangle
interface IPolygonG4 {} // gouraud-shaded quad
interface IPolygonGT3 {} // gouraud-shaded textured triangle
interface IPolygonGT4 {} // gouraud-shaded textured quad
... // other primitives interfaces, e.g. lights, sprites

class Polygon : IPolygon {}
class PolygonType1 : Polygon, IPolygonFT3 {}
... // about 3/4 types of polygons per family

Once I figured out I was using this very pattern, I searched online and found politically correct answers for most.

Basically, they'd say, it's a very very bad pattern, and either provide no alternatives or terrible ones performance-wise, e.g. add attributes you'd query at run-time using reflection…

Been thinking this inside out and nope, it is indeed the right pattern for this use case; every other I thought about were either bloated or not as performant or terse, e.g. check arrays counts, implement N base classes, etc.

In fact, it works so well that as I'm refactoring I realized that I could further split these up, e.g. add IPolygonF, IPolygonG, IPolygon3, IPolygon4; this would further simplify callers.

So what do you think? Good pattern or not? If not, then what else?

Advertisement

Don't do that, it's an abomination.

It is much simpler (less code) and faster to pack all things into an array with data members that control how it is interpreted. That way you avoid the combinatorial explosion of code. Such as:

class PolygonBuffer
{
	int verticesPerPolygon;
	int polygonCount;
	float [] vertices;
	float [] normals;
	float [] uvs;
	bool textured; // this should go in a material class instead
	bool gouraud; // this should go in a material class instead
}

You most definitely don't want to have a single class object per polygon. It will be much faster to put all polygons into the same array.

Not sure if I get you right… If I understand correctly, the role of PolygonBuffer would be to hold the content of N polygons? Why for exactly? Just because it might be faster to process?

I should have added more detail in fact…

The different game versions, although primitives have different formats, use more or less the same container:

class Model
{
	string Name;
	Vector3 Position;
	IPrimitive[] Primitives; // IPolygon is IPrimitive, there can be ILight, ISprite, etc
	// ...
}

l code around the existing, the existing structure is a pretty good abstraction IMO and is easy to use.
Currently, if I need to grab just polygons, I do so using LINQ's .OfType<IPolygon>(), that's all.

Should have mentioned that these games did their animation based on polygons… I have a helper that uses a predicate to return positions of polygons in a mesh, then the existing polygon-based animation logic only needs minor changes. This approach allowed me to re-implement things very quickly with few changes.

That's the simplest and most effective way I've found, at the expense of marker interfaces, yes…
And as for performance, it's irrelevant as the game content is ridiculous compared to today's standards.

Maybe I misunderstood what you've suggested…

This topic is closed to new replies.

Advertisement