Generate a plan from model

Started by Pignic, October 31, 2012, 05:43:47 PM

Previous topic - Next topic

Pignic

Hi,
I am looking for a simple way to get a shape that represent a plain slice of a model.
For example : if I have the walls of a house, I want to be able to get the ground inside the house as a model.
Are those kinds of uses are native in jpct? or will I explode my brain on tons of big algorythm ?

Thanks and best regards,

Nicolas.

Pignic

Or most easily, how to generate a flat model with a list of points in the same plan ?

Thanks.

EgonOlsen

So...you basically have an outline and want to triangulate the area inside (like everything between walls)? There's no support for something like this, you would have to do that on your own. If you want to, you should read something about triangulation...but it won't be that simple, i promise... ;)

About your second post: I'm not sure what you mean exactly? How to place a simple plane below the house? If that's the question, then just do it. You can get a plane from the Primitives class. You might have to rotate it around X to make it flat. Then adjust it in a way that it's below the house. Either by trial-and-error or by getting the bounding box from the house's Mesh and use that you position the plane below it.

Pignic

#3
I created a fairly simple algorithm to answer my question.
I don't think it is very efficient but it works pretty well.
I think the functions that I have added can be bypassed by JPCT functions easier to use.

The class is called ModelComposerImpl.

The main function :

public static Object3D generateFlatModel(final List<Object3D> inPoints,
final float height, final int groundTextureId,
final int roofTextureId) {
final Object3D plan = new Object3D(1000);
final List<Object3D> points = new ArrayList<Object3D>(inPoints);
int i = 0;
// The angle to know if a corner is in or out the shape.
final float innerAngle = ModelComposerImpl.calcInnerAngle(inPoints);
// The vector to lift the plan by the given value.
final SimpleVector liftVector = new SimpleVector(0, 0, -(inPoints
.get(0).getOrigin().z - height));
final float textureRatio = 128;
// While we can make triangles
while (points.size() > 2) {
final int size = points.size();
// Get the angle of the corner
final float angle = ModelComposerImpl.getAngle(points.get(i % size)
.getOrigin(), points.get((i + 1) % size).getOrigin(),
points.get((i + 2) % size).getOrigin());
// If the corner is an obtuse angle
if (angle <= 0 && innerAngle > 0 || angle >= 0 && innerAngle < 0) {
++i;
continue;
}
boolean intersected = false;
// Check if the triangle overlaps another side of the shape
for (int j = 0; j < inPoints.size() && !intersected; ++j) {
intersected = ModelComposerImpl.isLineCrossing(
points.get(i % size).getOrigin(),
points.get((i + 2) % size).getOrigin(),
inPoints.get(j % inPoints.size()).getOrigin(), inPoints
.get((j + 1) % inPoints.size()).getOrigin());
}
// If the triangle does not overlaps another side of the shape,
// create the triangle.
if (!intersected) {
final SimpleVector point1 = points.get((i + 2) % size)
.getOrigin().calcAdd(liftVector);
final SimpleVector point2 = points.get((i + 1) % size)
.getOrigin().calcAdd(liftVector);
final SimpleVector point3 = points.get(i % size).getOrigin()
.calcAdd(liftVector);
// Upper face
plan.addTriangle(point1, point1.x / textureRatio, point1.y
/ textureRatio, point2, point2.x / textureRatio,
point2.y / textureRatio, point3, point3.x
/ textureRatio, point3.y / textureRatio,
innerAngle > 0 ? groundTextureId : roofTextureId);
// Lower face
plan.addTriangle(point3, point3.x / textureRatio, point3.y
/ textureRatio, point2, point2.x / textureRatio,
point2.y / textureRatio, point1, point1.x
/ textureRatio, point1.y / textureRatio,
innerAngle > 0 ? roofTextureId : groundTextureId);
points.remove((i + 1) % size);
} else {
++i;
}
}
return plan;
}


The function to get the an angle between two vectors (it's to bad that I don't found a simple method to get an oriented angle so I do it manualy) :


public static float getAngle(final SimpleVector side1,
final SimpleVector center, final SimpleVector side2) {
final SimpleVector side1Vector = side1.calcSub(center);
final SimpleVector side2Vector = side2.calcSub(center);
float angle = side1.calcSub(center).calcAngle(side2.calcSub(center));
// If the angle is an obtuse angle, subtract PI to the angle
if (side1Vector.x < side2Vector.x && side1Vector.y > side2Vector.y
&& side1Vector.y > 0 && side2Vector.x > 0) {
angle -= Math.PI;
}
if (side1Vector.x < side2Vector.x && side1Vector.y < side2Vector.y
&& side1Vector.x < 0 && side2Vector.y > 0) {
angle -= Math.PI;
}
if (side1Vector.x > side2Vector.x && side1Vector.y < side2Vector.y
&& side1Vector.y < 0 && side2Vector.x < 0) {
angle -= Math.PI;
}
if (side1Vector.x > side2Vector.x && side1Vector.y > side2Vector.y
&& side1Vector.x > 0 && side2Vector.y < 0) {
angle -= Math.PI;
}
return angle;
}


The function to check if two segments are crossing (return false if the segments get a same point)
I think this function can be replaced by a jpct's function but I don't find it.

public static boolean isLineCrossing(final SimpleVector line1Origin,
final SimpleVector line1End, final SimpleVector line2Origin,
final SimpleVector line2End) {
// If the lines have a same point , return false.
if (line1Origin.distance(line2Origin) == 0
|| line1Origin.distance(line2End) == 0
|| line1End.distance(line2Origin) == 0
|| line1End.distance(line2End) == 0) {
return false;
}
double sx;
double sy;
if (line1Origin.x == line1End.x) {
if (line2Origin.x == line2End.x) {
return false;
} else {
final double pCD = (line2Origin.y - line2End.y)
/ (line2Origin.x - line2End.x);
sx = line1Origin.x;
sy = pCD * (line1Origin.x - line2Origin.x) + line2Origin.y;
}
} else {
if (line2Origin.x == line2End.x) {
final double pAB = (line1Origin.y - line1End.y)
/ (line1Origin.x - line1End.x);
sx = line2Origin.x;
sy = pAB * (line2Origin.x - line1Origin.x) + line1Origin.y;
} else {
final double pCD = (line2Origin.y - line2End.y)
/ (line2Origin.x - line2End.x);
final double pAB = (line1Origin.y - line1End.y)
/ (line1Origin.x - line1End.x);
if (pAB == pCD) {
// If the vectors are collinear, we don't need to consider that they colliding.
return false;
}
final double oCD = line2Origin.y - pCD * line2Origin.x;
final double oAB = line1Origin.y - pAB * line1Origin.x;
sx = (oAB - oCD) / (pCD - pAB);
sy = pCD * sx + oCD;
}
}
// if sx and sy are not in the lines, return false.
if ((sx < line1Origin.x && sx < line1End.x)
| (sx > line1Origin.x && sx > line1End.x)
| (sx < line2Origin.x && sx < line2End.x)
| (sx > line2Origin.x && sx > line2End.x)
| (sy < line1Origin.y && sy < line1End.y)
| (sy > line1Origin.y && sy > line1End.y)
| (sy < line2Origin.y && sy < line2End.y)
| (sy > line2Origin.y && sy > line2End.y)) {
return false;
}
return true;
}


The function to get the inner angle of the shape :

public static float calcInnerAngle(final List<Object3D> points) {
float angle = 0;
final int size = points.size();
for (int i = 0; i < points.size(); ++i) {
angle += ModelComposerImpl.getAngle(points.get(i).getOrigin(),
points.get((i + 1) % size).getOrigin(),
points.get((i + 2) % size).getOrigin());
}
return angle / 4;
}


And then :


Thanks EgonOlsen for your reactivity !

Best regards.

EgonOlsen

Nice. This might be handy for some people, thanks for posting the actual code.