use of com.jme3.scene.plugins.blender.meshes.Face in project jmonkeyengine by jMonkeyEngine.
the class OBJLoader method constructMesh.
protected Mesh constructMesh(ArrayList<Face> faceList) {
Mesh m = new Mesh();
m.setMode(Mode.Triangles);
boolean hasTexCoord = false;
boolean hasNormals = false;
ArrayList<Face> newFaces = new ArrayList<Face>(faceList.size());
for (int i = 0; i < faceList.size(); i++) {
Face f = faceList.get(i);
for (Vertex v : f.verticies) {
findVertexIndex(v);
if (!hasTexCoord && v.vt != null)
hasTexCoord = true;
if (!hasNormals && v.vn != null)
hasNormals = true;
}
if (f.verticies.length == 4) {
Face[] t = quadToTriangle(f);
newFaces.add(t[0]);
newFaces.add(t[1]);
} else {
newFaces.add(f);
}
}
FloatBuffer posBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
FloatBuffer normBuf = null;
FloatBuffer tcBuf = null;
if (hasNormals) {
normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
}
if (hasTexCoord) {
tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2);
m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
}
IndexBuffer indexBuf = null;
if (vertIndexMap.size() >= 65536) {
// too many verticies: use intbuffer instead of shortbuffer
IntBuffer ib = BufferUtils.createIntBuffer(newFaces.size() * 3);
m.setBuffer(VertexBuffer.Type.Index, 3, ib);
indexBuf = new IndexIntBuffer(ib);
} else {
ShortBuffer sb = BufferUtils.createShortBuffer(newFaces.size() * 3);
m.setBuffer(VertexBuffer.Type.Index, 3, sb);
indexBuf = new IndexShortBuffer(sb);
}
int numFaces = newFaces.size();
for (int i = 0; i < numFaces; i++) {
Face f = newFaces.get(i);
if (f.verticies.length != 3)
continue;
Vertex v0 = f.verticies[0];
Vertex v1 = f.verticies[1];
Vertex v2 = f.verticies[2];
posBuf.position(v0.index * 3);
posBuf.put(v0.v.x).put(v0.v.y).put(v0.v.z);
posBuf.position(v1.index * 3);
posBuf.put(v1.v.x).put(v1.v.y).put(v1.v.z);
posBuf.position(v2.index * 3);
posBuf.put(v2.v.x).put(v2.v.y).put(v2.v.z);
if (normBuf != null) {
if (v0.vn != null) {
normBuf.position(v0.index * 3);
normBuf.put(v0.vn.x).put(v0.vn.y).put(v0.vn.z);
normBuf.position(v1.index * 3);
normBuf.put(v1.vn.x).put(v1.vn.y).put(v1.vn.z);
normBuf.position(v2.index * 3);
normBuf.put(v2.vn.x).put(v2.vn.y).put(v2.vn.z);
}
}
if (tcBuf != null) {
if (v0.vt != null) {
tcBuf.position(v0.index * 2);
tcBuf.put(v0.vt.x).put(v0.vt.y);
tcBuf.position(v1.index * 2);
tcBuf.put(v1.vt.x).put(v1.vt.y);
tcBuf.position(v2.index * 2);
tcBuf.put(v2.vt.x).put(v2.vt.y);
}
}
// current face * 3 = current index
int index = i * 3;
indexBuf.put(index, v0.index);
indexBuf.put(index + 1, v1.index);
indexBuf.put(index + 2, v2.index);
}
m.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
// index buffer and others were set on creation
m.setStatic();
m.updateBound();
m.updateCounts();
//m.setInterleaved();
// clear data generated face statements
// to prepare for next mesh
vertIndexMap.clear();
indexVertMap.clear();
curIndex = 0;
return m;
}
use of com.jme3.scene.plugins.blender.meshes.Face in project jmonkeyengine by jMonkeyEngine.
the class LodGenerator method computeEdgeCollapseCost.
float computeEdgeCollapseCost(Vertex src, Edge dstEdge) {
// This is based on Ogre's collapse cost calculation algorithm.
Vertex dest = dstEdge.destination;
// then this would destroy the shape, so don't do this
if (src.triangles.size() == 1 && dest.triangles.size() == 1) {
return NEVER_COLLAPSE_COST;
}
// Look for a face normal changing by > 90 degrees
for (Triangle triangle : src.triangles) {
// Ignore the deleted faces (those including src & dest)
if (!triangle.hasVertex(dest)) {
// Test the new face normal
Vertex pv0, pv1, pv2;
// Replace src with dest wherever it is
pv0 = (triangle.vertex[0] == src) ? dest : triangle.vertex[0];
pv1 = (triangle.vertex[1] == src) ? dest : triangle.vertex[1];
pv2 = (triangle.vertex[2] == src) ? dest : triangle.vertex[2];
// Cross-product 2 edges
tmpV1.set(pv1.position).subtractLocal(pv0.position);
tmpV2.set(pv2.position).subtractLocal(pv1.position);
//computing the normal
Vector3f newNormal = tmpV1.crossLocal(tmpV2);
newNormal.normalizeLocal();
// If < 0 then more than 90 degree difference
if (newNormal.dot(triangle.normal) < 0.0f) {
// Don't do it!
return NEVER_COLLAPSE_COST;
}
}
}
float cost;
// If we're looking at a border vertex
if (isBorderVertex(src)) {
if (dstEdge.refCount > 1) {
// src is on a border, but the src-dest edge has more than one tri on it
// So it must be collapsing inwards
// Mark as very high-value cost
// curvature = 1.0f;
cost = 1.0f;
} else {
// Collapsing ALONG a border
// We can't use curvature to measure the effect on the model
// Instead, see what effect it has on 'pulling' the other border edges
// The more colinear, the less effect it will have
// So measure the 'kinkiness' (for want of a better term)
// Find the only triangle using this edge.
// PMTriangle* triangle = findSideTriangle(src, dst);
cost = 0.0f;
Vector3f collapseEdge = tmpV1.set(src.position).subtractLocal(dest.position);
collapseEdge.normalizeLocal();
for (Edge edge : src.edges) {
Vertex neighbor = edge.destination;
//reference check intended
if (neighbor != dest && edge.refCount == 1) {
Vector3f otherBorderEdge = tmpV2.set(src.position).subtractLocal(neighbor.position);
otherBorderEdge.normalizeLocal();
// This time, the nearer the dot is to -1, the better, because that means
// the edges are opposite each other, therefore less kinkiness
// Scale into [0..1]
float kinkiness = (otherBorderEdge.dot(collapseEdge) + 1.002f) * 0.5f;
cost = Math.max(cost, kinkiness);
}
}
}
} else {
// not a border
// Standard inner vertex
// Calculate curvature
// use the triangle facing most away from the sides
// to determine our curvature term
// Iterate over src's faces again
cost = 0.001f;
for (Triangle triangle : src.triangles) {
// curve for face i and closer side to it
float mincurv = 1.0f;
for (Triangle triangle2 : src.triangles) {
if (triangle2.hasVertex(dest)) {
// Dot product of face normal gives a good delta angle
float dotprod = triangle.normal.dot(triangle2.normal);
// NB we do (1-..) to invert curvature where 1 is high curvature [0..1]
// Whilst dot product is high when angle difference is low
mincurv = Math.min(mincurv, (1.002f - dotprod) * 0.5f);
}
}
cost = Math.max(cost, mincurv);
}
}
// check for texture seam ripping
if (src.isSeam) {
if (!dest.isSeam) {
cost += meshBoundingSphereRadius;
} else {
cost += meshBoundingSphereRadius * 0.5;
}
}
return cost * src.position.distanceSquared(dest.position);
}
use of com.jme3.scene.plugins.blender.meshes.Face in project chordatlas by twak.
the class GreebleSkel method createMesh.
public void createMesh(Output output) {
float[] roofColor = new float[] { 0.3f, 0.3f, 0.3f, 1 }, wallColor = new float[] { 228 / 255f, 223 / 255f, 206 / 255f, 1.0f };
if (output.faces == null)
return;
double bestWallArea = 0, bestRoofArea = 0;
for (Face f : output.faces.values()) {
double area = Loopz.area3(f.getLoopL());
Tag t = getTag(f.profile, RoofTag.class);
if (t != null && area > bestRoofArea && ((RoofTag) t).color != null) {
roofColor = ((RoofTag) t).color;
bestRoofArea = area;
}
t = getTag(f.profile, WallTag.class);
if (t != null && area > bestWallArea && ((WallTag) t).color != null) {
wallColor = ((WallTag) t).color;
bestWallArea = area;
}
}
greebleGrid = new GreebleGrid(tweed, mbs = new MMeshBuilderCache());
output.addNonSkeletonSharedEdges(new RoofTag(roofColor));
edges(output, roofColor);
for (List<Face> chain : Campz.findChains(output)) {
// for ( Face f : output.faces.values() )
// mbs.get(roofColor).add3d( Loopz.insertInnerEdges( f.getLoopL() ), zToYup );
Optional<Tag> opt = chain.stream().flatMap(f -> f.profile.stream()).filter(tag -> tag instanceof WallTag).findAny();
WallTag wt = null;
Set<QuadF> features = new HashSet<>();
MiniFacade mf = null;
if (opt.isPresent() && (wt = (WallTag) opt.get()).miniFacade != null) {
MiniFacade mf2 = new MiniFacade(wt.miniFacade);
Line facadeLine;
{
Edge e = chain.get(0).edge;
// we might rotate the facade to apply a set of features to a different side of the building.
facadeLine = new Line(e.end.x, e.end.y, e.start.x, e.start.y);
}
if (TweedSettings.settings.snapFacadeWidth) {
// move/scale mf horizontally from mean-image-location to mesh-facade-location
double[] meshSE = findSE(wt.miniFacade, facadeLine, chain);
mf2.scaleX(meshSE[0], meshSE[1]);
}
// find window locations in 3 space
mf2.rects.values().stream().flatMap(f -> f.stream()).map(r -> new QuadF(r, facadeLine)).forEach(q -> features.add(q));
mf = mf2;
}
for (Face f : chain) {
face(f, mf, features, roofColor, wallColor);
}
for (QuadF w : features) if ((w.original.f == Feature.WINDOW || w.original.f == Feature.SHOP) && w.foundAll()) {
greebleGrid.createDormerWindow(w, mbs.WOOD, mbs.GLASS, (float) wt.sillDepth, (float) wt.sillHeight, (float) wt.corniceHeight, 0.6, 0.9);
}
// for ( String mName : mbs.cache.keySet() )
// for (float[] mCol : mbs.cache.get( mName ).keySet() )
// node.attachChild( mb2Geom( output, chain, mName, mCol ) );
greebleGrid.attachAll(node, chain, output, new ClickMe() {
@Override
public void clicked(Object data) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
selected(output, node, findSuperEdge(output, chain));
}
});
} catch (Throwable th) {
th.printStackTrace();
}
}
});
}
}
use of com.jme3.scene.plugins.blender.meshes.Face in project chordatlas by twak.
the class ProfileGen method findMegaFaces.
private List<MegaFacade> findMegaFaces(double delta) {
SliceParameters P = new SliceParameters(SLICE_SCALE);
List<LineAtHeight> lines = new ArrayList();
Map<Integer, LineSoup> slices = new HashMap<>();
for (int hi = 0; getHeight(hi) < extent[3]; hi++) {
int _i = hi;
boolean error;
int count = 0;
do {
error = false;
try {
LineSoup soup = new LineSoup(ObjSlice.sliceTri(blockGen.getCroppedMesh(), getHeight(hi), majorAxis));
FindLines foundLines = new FindLines(soup, TweedSettings.settings.useGis ? gisBias : null, -1, null, P);
foundLines.result.all.stream().forEach(x -> lines.add(new LineAtHeight(_i, delta, x, foundLines.result.all)));
slices.put(hi, foundLines.result);
} catch (Throwable th) {
th.printStackTrace();
error = true;
}
} while (error && count++ < 10);
}
Collections.sort(lines, new LAHComparator());
// new Plot(lines.stream().map (lah -> lah.line).collect(Collectors.toList() ), gis);
faces = new ArrayList<>();
while (!lines.isEmpty()) {
System.out.println("clustering megafacdes " + lines.size());
// longest line
LineAtHeight start = lines.get(0);
lines.remove(start);
MegaFacade face = new MegaFacade(start);
Set<LineAtHeight> toProcess = new HashSet<>();
toProcess.add(start);
while (!toProcess.isEmpty()) {
LineAtHeight lah = toProcess.iterator().next();
toProcess.remove(lah);
for (int _pmDelta : new int[] { -2, -1, 1, 2 }) {
int hi = lah.height + _pmDelta;
LineSoup ls = slices.get(hi);
if (ls != null) {
for (Line l : ls.all) {
double lps = start.line.findPPram(l.start), lpe = start.line.findPPram(l.end);
double overlap = 0;
if (lpe < lps)
overlap = 0;
else if (lps > face.minP && lpe < face.maxP || lps < face.minP && lpe > face.maxP)
overlap = 1;
else if (lps < face.minP && lpe > face.minP)
overlap = (lpe - face.minP) / (lpe - lps);
else if (lps < face.maxP && lpe > face.maxP)
overlap = (face.maxP - lps) / (lpe - lps);
double angle = l.absAngle(start.line);
if (overlap > 0.8 && angle < 0.3 || overlap > 0.5 && /* 0.1 for aggressive clustering */
angle < 0.1) {
// if (overlap > 0.1 && angle < 0.5 ) {
if (l.distance(lah.line) < delta * TweedSettings.settings.megafacacadeClusterGradient) {
LineAtHeight toProc = new LineAtHeight(hi, l);
if (lines.contains(toProc)) {
toProcess.add(toProc);
face.put(hi, l);
lines.remove(toProc);
// bit strange: depends on the processing order of the set
face.minP = Mathz.min(face.minP, lps);
face.maxP = Mathz.max(face.maxP, lpe);
}
}
}
}
}
}
}
if (face.values().stream().flatMap(x -> x.stream()).count() > 5)
faces.add(face);
else
System.out.println("skipping small megafacade");
}
Collections.sort(faces);
processMegaFaces();
return faces;
}
Aggregations