Three.js - can I detect geometry 'islands' when importing? - three.js

I'm importing .3DS models into Blender 2.72b, then exporting them with the Three.js import/export addon. The models have multiple geometry 'islands' (separate groups of connected faces and vertices), each with its own material. I'd like to be able to pair each material with its corresponding island, without having to create separate THREE.Geometry objects. After some digging, I found this question which suggests using a THREE.MeshFaceMaterial to achieve multiple materials for one object. The only problem is that the geometry in that example is a simple cube, whereas my models have hundreds of faces spread across 2-5 islands.
Does Three.js have functionality for identifying geometry 'islands' in a mesh?

No. three.js does not have functionality for identifying geometry 'islands' in a mesh.
When using MeshFaceMaterial, WebGLRenderer breaks the geometry into chunks anyway -- one chunk for each material. It does that because WebGL supports one shader per geometry.
I would not merge all your geometries, and then use MeshFaceMaterial, only to have the renderer break the single geometry apart.
You can merge geometries that share a single material if you wish.
three.js r.69

I tried with a function but still is not accurate, it produce more geometries than non connected geometries:
If somebody could have a look on it, it would be grate.
function groupGeometryIntoNonConnectedGeometries(geometry) {
const material = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
vertexColors: THREE.VertexColors
});
let geometryArray = [];
const indexArray = geometry.index.array;
const positionArray = geometry.attributes.position.array;
const positionCount = geometry.attributes.position.count;
const color = new THREE.Vector3(geometry.attributes.color.array[0], geometry.attributes.color.array[1], geometry.attributes.color.array[2]);
const totalTriangles = indexArray.length / 3;
let geometryCount = 0;
let indexValueAlreadyVisited = new Uint8Array(indexArray.length);
let structure = [];
/*
* indexValue: {
* child: [ [indexval0, indexval1], [] ],
* parent: null
* }
*/
// Initialize Structure:
for (var vextexIdx=0; vextexIdx<positionCount; vextexIdx++) {
structure[vextexIdx] = {
child: [],
parent: null
}
}
for (idx=0; idx<totalTriangles; idx++) {
const geoIndex1 = indexArray[idx*3];
const geoIndex2 = indexArray[idx*3+1];
const geoIndex3 = indexArray[idx*3+2];
const triangleIndexVertexArray = [ geoIndex1, geoIndex2, geoIndex3 ].sort(function(a, b) {
return a - b;
});
structure[ triangleIndexVertexArray[0] ].child.push(triangleIndexVertexArray[1], triangleIndexVertexArray[2]);
structure[ triangleIndexVertexArray[1] ].parent = triangleIndexVertexArray[0];
structure[ triangleIndexVertexArray[2] ].parent = triangleIndexVertexArray[0];
}
let count = 0;
let currentCount = 0;
let geometryStructureArray = [];
for (let strIdx=0; strIdx<structure.length; strIdx++) {
if (structure[strIdx].parent == null) {
currentCount = count;
geometryStructureArray[currentCount] = {
name: "G_" + currentCount,
indexMap: {},
currentIndex: 0,
indexArray: [],
positionArray: [],
colorArray: []
};
count += 1;
}
if (structure[strIdx].child.length > 0) {
const childLen = structure[strIdx].child.length / 2;
for (let childIdx=0; childIdx<childLen; childIdx++) {
const vertexIndex0 = strIdx;
const vertexIndex1 = structure[strIdx].child[childIdx*2];
const vertexIndex2 = structure[strIdx].child[childIdx*2+1];
const v0 = new THREE.Vector3( positionArray[strIdx*3], positionArray[strIdx*3+1], positionArray[strIdx*3+2] );
const v1 = new THREE.Vector3( positionArray[vertexIndex1*3], positionArray[vertexIndex1*3+1], positionArray[vertexIndex1*3+2] );
const v2 = new THREE.Vector3( positionArray[vertexIndex2*3], positionArray[vertexIndex2*3+1], positionArray[vertexIndex2*3+2] );
// check vertex0
if (geometryStructureArray[currentCount].indexMap[vertexIndex0] == undefined) {
geometryStructureArray[currentCount].indexMap[vertexIndex0] = geometryStructureArray[currentCount].currentIndex;
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].currentIndex);
geometryStructureArray[currentCount].positionArray.push(v0.x, v0.y, v0.z);
geometryStructureArray[currentCount].colorArray.push(color.x, color.y, color.z);
geometryStructureArray[currentCount].currentIndex += 1;
} else {
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].indexMap[vertexIndex0]);
}
// check vertex1
if (geometryStructureArray[currentCount].indexMap[vertexIndex1] == undefined) {
geometryStructureArray[currentCount].indexMap[vertexIndex1] = geometryStructureArray[currentCount].currentIndex;
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].currentIndex);
geometryStructureArray[currentCount].positionArray.push(v1.x, v1.y, v1.z);
geometryStructureArray[currentCount].colorArray.push(color.x, color.y, color.z);
geometryStructureArray[currentCount].currentIndex += 1;
} else {
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].indexMap[vertexIndex1]);
}
// check vertex1
if (geometryStructureArray[currentCount].indexMap[vertexIndex2] == undefined) {
geometryStructureArray[currentCount].indexMap[vertexIndex2] = geometryStructureArray[currentCount].currentIndex;
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].currentIndex);
geometryStructureArray[currentCount].positionArray.push(v2.x, v2.y, v2.z);
geometryStructureArray[currentCount].colorArray.push(color.x, color.y, color.z);
geometryStructureArray[currentCount].currentIndex += 1;
} else {
geometryStructureArray[currentCount].indexArray.push(geometryStructureArray[currentCount].indexMap[vertexIndex2]);
}
}
}
}
// Convert to geometryArray:
const geometryStructureArrayLen = geometryStructureArray.length;
const object3d = new THREE.Object3D();
for (let geoIdx=0; geoIdx<geometryStructureArrayLen; geoIdx++) {
const geo = new THREE.BufferGeometry();
geo.name = "G_" + geoIdx;
const geoPositions = new Float32Array(geometryStructureArray[geoIdx].positionArray);
const geoColors = new Float32Array(geometryStructureArray[geoIdx].colorArray);
const geoIndices = new Uint32Array(geometryStructureArray[geoIdx].indexArray);
//console.log(geoIdx, "geoPositions: ", geoPositions);
//console.log(geoIdx, "geoColors: ", geoColors);
//console.log(geoIdx, "geoIndices: ", geoIndices);
geo.index = new THREE.BufferAttribute(geoIndices, 1, false);
geo.attributes.position = new THREE.BufferAttribute(geoPositions, 3, false);
geo.attributes.color = new THREE.BufferAttribute(geoColors, 3, true);
geo.computeBoundingSphere();
geo.computeBoundingBox();
const mesh = new THREE.Mesh(geo, material);
mesh.name = "M_" + geoIdx;
object3d.add(mesh);
}
//return [structure, geometryStructureArray, object3d, count];
return object3d;
}
Best regards

This is I think the correct way:
function unmergeGeometryArray(geometry) {
// Asumptions:
// geometry is BufferGeometry
// The geometry has no index duplicates (2 equal positions with different index) neither empty triangles, the geometry has been processed with mergeVertices function
// normal attribute is discarded, can be recomputed after, only color and position attributes are taken into account
const material = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
vertexColors: THREE.VertexColors
});
const indexArray = geometry.index.array;
const positionArray = geometry.attributes.position.array;
const positionCount = geometry.attributes.position.count;
const totalTriangles = indexArray.length / 3;
let triangleVisitedArray = new Uint8Array(totalTriangles);
let indexVisitedArray = new Uint8Array(positionCount);
let indexToTriangleIndexMap = [];
let missingVertices = positionCount;
let missingTriangles = totalTriangles;
// Functions:
function computeTrianglesRecursive(index, out){
//console.log("- start of computeTriangles with index:", index);
if (indexVisitedArray[index] === 1 || missingVertices === 0 || missingTriangles === 0) {
return;
}
indexVisitedArray[index] = 1;
missingVertices -= 1;
let triangleIndexArray = indexToTriangleIndexMap[index];
for(let i=0; i<indexToTriangleIndexMap[index].length; i++) {
let triangleIndex = indexToTriangleIndexMap[index][i];
if (triangleVisitedArray[triangleIndex] === 0) {
triangleVisitedArray[triangleIndex] = 1
missingTriangles -= 1;
//console.log("-- index: ", index, "; i: ", i, "; triangleIndex: ", triangleIndex);
out.push(triangleIndex);
childIndex1 = indexArray[triangleIndex*3+1];
computeTriangles(childIndex1, out);
childIndex2 = indexArray[triangleIndex*3+2];
computeTriangles(childIndex2, out);
}
}
}
function computeTriangles(indexTocheck){
let out = [];
let startIndex = indexTocheck;
let indexToCheckArray = [indexTocheck];
let i = 0;
while (i<indexToCheckArray.length) {
let index = indexToCheckArray[i];
if (indexVisitedArray[index] == 0) {
indexVisitedArray[index] = 1;
missingVertices -= 1;
let triangleIndexArray = indexToTriangleIndexMap[index];
for(let j=0; j<indexToTriangleIndexMap[index].length; j++) {
let triangleIndex = indexToTriangleIndexMap[index][j];
if (triangleVisitedArray[triangleIndex] === 0) {
triangleVisitedArray[triangleIndex] = 1;
missingTriangles -= 1;
out.push(triangleIndex);
let rootIndex = indexArray[triangleIndex*3];
let child1Index = indexArray[triangleIndex*3+1];
let child2Index = indexArray[triangleIndex*3+2];
if (indexToCheckArray.indexOf(rootIndex) === -1) {
indexToCheckArray.push(rootIndex);
}
if (indexToCheckArray.indexOf(child1Index) === -1) {
indexToCheckArray.push(child1Index);
}
if (indexToCheckArray.indexOf(child2Index) === -1) {
indexToCheckArray.push(child2Index);
}
}
}
}
i +=1;
}
return out;
}
// In the first loop we reorder indices asc order + generate map
for (triangleIndex=0; triangleIndex<totalTriangles; triangleIndex++) {
const geoIndex1 = indexArray[triangleIndex*3];
const geoIndex2 = indexArray[triangleIndex*3+1];
const geoIndex3 = indexArray[triangleIndex*3+2];
const triangleIndexVertexArray = [ geoIndex1, geoIndex2, geoIndex3 ].sort(function(a, b) {
return a - b;
});
if (indexToTriangleIndexMap[geoIndex1] === undefined) {
indexToTriangleIndexMap[geoIndex1] = [triangleIndex];
} else {
indexToTriangleIndexMap[geoIndex1].push(triangleIndex);
}
if (indexToTriangleIndexMap[geoIndex2] === undefined) {
indexToTriangleIndexMap[geoIndex2] = [triangleIndex];
} else {
indexToTriangleIndexMap[geoIndex2].push(triangleIndex);
}
if (indexToTriangleIndexMap[geoIndex3] === undefined) {
indexToTriangleIndexMap[geoIndex3] = [triangleIndex];
} else {
indexToTriangleIndexMap[geoIndex3].push(triangleIndex);
}
//indexArray[triangleIndex*3] = triangleIndexVertexArray[0];
//indexArray[triangleIndex*3+1] = triangleIndexVertexArray[1];
//indexArray[triangleIndex*3+2] = triangleIndexVertexArray[2];
}
let geometryTriangleArray = [];
let index = 0;
while (index<indexToTriangleIndexMap.length && missingVertices>0 && missingTriangles>0){
let out = [];
if (indexVisitedArray[index] === 0) {
out = computeTriangles(index);
}
if (out.length > 0) {
geometryTriangleArray.push(out);
}
index += 1;
}
let geometryArray = [];
for (let i=0; i<geometryTriangleArray.length; i++) {
let out = {
positionArray: [],
colorArray: [],
indexArray: [],
indexMap: [],
currentIndex: 0
}
let triangleArray = geometryTriangleArray[i];
for (let j=0; j<triangleArray.length; j++) {
let triangleIndex = triangleArray[j];
let rootIndex = indexArray[triangleIndex*3];
if (out.indexMap[rootIndex] === undefined) {
out.indexMap[rootIndex] = out.currentIndex;
// add vertex position and color
out.positionArray.push(
geometry.attributes.position.array[rootIndex*3],
geometry.attributes.position.array[rootIndex*3+1],
geometry.attributes.position.array[rootIndex*3+2]
);
if (geometry.attributes.color != undefined) {
out.colorArray.push(
geometry.attributes.color.array[rootIndex*3],
geometry.attributes.color.array[rootIndex*3+1],
geometry.attributes.color.array[rootIndex*3+2]
);
}
out.currentIndex += 1;
}
let child1Index = indexArray[triangleIndex*3+1];
if (out.indexMap[child1Index] === undefined) {
out.indexMap[child1Index] = out.currentIndex;
// add vertex position and color
out.positionArray.push(
geometry.attributes.position.array[child1Index*3],
geometry.attributes.position.array[child1Index*3+1],
geometry.attributes.position.array[child1Index*3+2]
);
if (geometry.attributes.color != undefined) {
out.colorArray.push(
geometry.attributes.color.array[child1Index*3],
geometry.attributes.color.array[child1Index*3+1],
geometry.attributes.color.array[child1Index*3+2]
);
}
out.currentIndex += 1;
}
let child2Index = indexArray[triangleIndex*3+2];
if (out.indexMap[child2Index] === undefined) {
out.indexMap[child2Index] = out.currentIndex;
// add vertex position and color
out.positionArray.push(
geometry.attributes.position.array[child2Index*3],
geometry.attributes.position.array[child2Index*3+1],
geometry.attributes.position.array[child2Index*3+2]
);
if (geometry.attributes.color != undefined) {
out.colorArray.push(
geometry.attributes.color.array[child2Index*3],
geometry.attributes.color.array[child2Index*3+1],
geometry.attributes.color.array[child2Index*3+2]
);
}
out.currentIndex += 1;
}
// Add indices:
out.indexArray.push(out.indexMap[rootIndex], out.indexMap[child1Index], out.indexMap[child2Index]);
}
const geoPositions = new Float32Array(out.positionArray);
const geoColors = new Float32Array(out.colorArray);
const geoIndices = new Uint32Array(out.indexArray);
const geo = new THREE.BufferGeometry();
geo.name = "G_" + i;
geo.index = new THREE.BufferAttribute(geoIndices, 1, false);
geo.attributes.position = new THREE.BufferAttribute(geoPositions, 3, false);
geo.attributes.color = new THREE.BufferAttribute(geoColors, 3, true);
geo.computeBoundingSphere();
geo.computeBoundingBox();
geometryArray.push(geo);
}
return geometryArray;
}

Related

Ammo Threejs Breakable objects debis set material after breaking in fragments

i have initially breakable object with nice texture material loaded.
After breaking in fragments lost original material and load some default material.
Any suggest?
I use code from threejs example with ammo:
Source code:
import * as THREE from "three";
import {ConvexObjectBreaker} from "../jsm/misc/ConvexObjectBreaker";
import {updatePhysics} from "./updater";
export class MagicPhysics {
// Physics variables
gravityConstant = 7.8;
collisionConfiguration;
dispatcher;
broadphase;
solver;
physicsWorld;
margin = 0.05;
convexBreaker = new ConvexObjectBreaker();
// Rigid bodies include all movable objects
rigidBodies = [];
pos = new THREE.Vector3();
quat = new THREE.Quaternion();
transformAux1;
tempBtVec3_1;
objectsToRemove = [];
// Player
ammoTmpPos;
ammoTmpQuat;
tmpTrans;
numObjectsToRemove = 0;
impactPoint = new THREE.Vector3();
impactNormal = new THREE.Vector3();
// kinekt type of movement
kMoveDirection = {left: 0, right: 0, forward: 0, back: 0};
// velocity type of movement
moveDirection = {left: 0, right: 0, forward: 0, back: 0};
tmpPos = new THREE.Vector3();
tmpQuat = new THREE.Quaternion();
constructor(options) {
console.log("MagicPhysics =>", options)
this.updatePhysics = updatePhysics.bind(this);
this.config = options.config;
}
initPhysics() {
// Physics configuration
this.collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
this.dispatcher = new Ammo.btCollisionDispatcher(this.collisionConfiguration);
this.broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
this.physicsWorld = new Ammo.btDiscreteDynamicsWorld(
this.dispatcher,
this.broadphase,
solver,
this.collisionConfiguration
);
this.physicsWorld.setGravity(new Ammo.btVector3(0, -this.gravityConstant, 0));
this.transformAux1 = new Ammo.btTransform();
this.tempBtVec3_1 = new Ammo.btVector3(0, 0, 0);
}
createRigidBody(object, physicsShape, mass, pos, quat, vel, angVel) {
if(pos) {
object.position.copy(pos);
} else {
pos = object.position;
}
if(quat) {
object.quaternion.copy(quat);
} else {
quat = object.quaternion;
}
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
transform.setRotation(
new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w)
);
const motionState = new Ammo.btDefaultMotionState(transform);
const localInertia = new Ammo.btVector3(0, 0, 0);
physicsShape.calculateLocalInertia(mass, localInertia);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(
mass,
motionState,
physicsShape,
localInertia
);
const body = new Ammo.btRigidBody(rbInfo);
body.setFriction(0.5);
if(vel) {
body.setLinearVelocity(new Ammo.btVector3(vel.x, vel.y, vel.z));
}
if(angVel) {
body.setAngularVelocity(
new Ammo.btVector3(angVel.x, angVel.y, angVel.z)
);
}
object.userData.physicsBody = body;
object.userData.collided = false;
this.scene.add(object);
if(mass > 0) {
this.rigidBodies.push(object);
// Disable deactivation
body.setActivationState(4);
}
this.physicsWorld.addRigidBody(body);
return body;
}
createConvexHullPhysicsShape(coords) {
const shape = new Ammo.btConvexHullShape();
for(let i = 0, il = coords.length;i < il;i += 3) {
this.tempBtVec3_1.setValue(coords[i], coords[i + 1], coords[i + 2]);
const lastOne = i >= il - 3;
shape.addPoint(this.tempBtVec3_1, lastOne);
}
return shape;
}
createParalellepipedWithPhysics(sx, sy, sz, mass, pos, quat, material) {
const object = new THREE.Mesh(
new THREE.BoxGeometry(sx, sy, sz, 1, 1, 1),
material
);
const shape = new Ammo.btBoxShape(
new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5)
);
shape.setMargin(this.margin);
this.createRigidBody(object, shape, mass, pos, quat);
return object;
}
createDebrisFromBreakableObject(object) {
object.castShadow = true;
object.receiveShadow = true;
const shape = this.createConvexHullPhysicsShape(
object.geometry.attributes.position.array
);
shape.setMargin(this.margin);
const body = this.createRigidBody(
object,
shape,
object.userData.mass,
null,
null,
object.userData.velocity,
object.userData.angularVelocity
);
// Set pointer back to the three object only in the debris objects
const btVecUserData = new Ammo.btVector3(0, 0, 0);
btVecUserData.threeObject = object;
body.setUserPointer(btVecUserData);
}
removeDebris(object) {
this.scene.remove(object);
this.physicsWorld.removeRigidBody(object.userData.physicsBody);
}
}

Three.JS - Object rotation

we have following function to create object wall:
function placeWorkareaWithSlope(points, waDetail, drawLine = true) {
const workareaHeight = Number(waDetail.WorkareaHeight) || 10;
const workareaParapet = Number(waDetail.WorkareaParapet) || 3;
// Different color wall for workarea
drawWallForWorkareaWithSlope(points, workareaHeight, waDetail);
drawWallBoxForWorkareaWithSlope(points, workareaHeight, waDetail);
// Convert points array to accrding to 3D space Quadrant
const convertedPoints = convertTo3dQuadrant(points);
const shape = [];
for (let i = 0; i < convertedPoints.length; i++) {
const e = convertedPoints[i];
shape.push(new THREE.Vector2(e.x, e.y));
}
const workareaShape = new THREE.Shape(shape);
const extrusionSettings = {
steps: 1,
depth: workareaHeight,
bevelEnabled: false,
};
const workareaGeometry = new THREE.ExtrudeGeometry(workareaShape, extrusionSettings);
workareaGeometry.computeVertexNormals();
workareaGeometry.computeFaceNormals();
const workarea = new THREE.Mesh(workareaGeometry, workareaMaterial);
workarea.rotation.x = -THREE.Math.degToRad(90);
//added by kd to define slope angle
objRotation(points, waDetail, workarea);
//end kd
workarea.receiveShadow = isRenderShaodw;
workarea.castShadow = isRenderWorkareaShadow;
scene.add(workarea);
// DrawLines
if (drawLine && workarea && workarea.geometry) {
const lineGeo = new THREE.EdgesGeometry(workarea.geometry);
const wireframe = new THREE.LineSegments(lineGeo, workareaLineMaterial);
wireframe.renderOrder = 1;
workarea.add(wireframe);
}
}
once we create wall over scene after that we rotate it base on slope provided with objRotation function with obj.rotation.y but when we rotate it at that movement object change its place, basically we require object should be there with world axis
following is object rotation function :
function objRotation(points, waDetail, obj) {
const Point1 = points[0];
const Point2 = points[1];
const Point3 = points[2];
const Point4 = points[3];
const slopeAngle = Number(waDetail.Angle) || 0;
if (waDetail.SlopeDirection == "P1 to P2") {
xDiff = Point1.x - Point2.x;
yDiff = Point1.y - Point2.y;
}
if (waDetail.SlopeDirection == "P2 to P3") {
xDiff = Point2.x - Point3.x;
yDiff = Point2.y - Point3.y;
}
if (waDetail.SlopeDirection == "P3 to P4") {
xDiff = Point3.x - Point4.x;
yDiff = Point3.y - Point4.y;
}
if (waDetail.SlopeDirection == "P4 to P1") {
xDiff = Point4.x - Point1.x;
yDiff = Point4.y - Point1.y;
}
if (Math.abs(xDiff) > Math.abs(yDiff)) {
if (obj.rotation.x > 0) {
if (xDiff > 0)
obj.rotation.y += THREE.Math.degToRad(slopeAngle);
else
obj.rotation.y -= THREE.Math.degToRad(slopeAngle);
}
else {
if (xDiff > 0)
obj.rotation.y -= THREE.Math.degToRad(slopeAngle);
else
obj.rotation.y += THREE.Math.degToRad(slopeAngle);
}
} else {
if (yDiff<0)
obj.rotation.x += THREE.Math.degToRad(slopeAngle);
else
obj.rotation.x -= THREE.Math.degToRad(slopeAngle);
}
}
So for the above issue, can someone guide me?
The wall created by the code with height and width provided by system and it rotates 90 degrees to setup over map surface but after that we have a requirement to slope its base on an angle so we added one more rotation there with provided slope angle by user. But when this slope angle rotation applied to object the object change its height base on camera left or right distance. We require the object should be same place there as before after rotation.

how do i make FlxAsyncLoop work for making a progress bar in HaxeFlixel?

so, i tried to make an async loop for my game (to make a progress bar) and the game crashes when the state loads...
i tried to change the loops position so they don't collide and all of the code is from the FlxAsyncLoop demo but with other variables and some other changes.
here's the code:
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.addons.util.FlxAsyncLoop;
import flixel.group.FlxGroup;
import flixel.math.FlxPoint;
import flixel.math.FlxVelocity;
import flixel.text.FlxText;
import flixel.ui.FlxBar;
import flixel.util.FlxColor;
class PlayState extends FlxState
{
public static var player:FlxSprite;
var ground:FlxSprite;
var axe:FlxSprite;
var wood:FlxSprite;
var base:FlxSprite;
var txt:FlxText;
var playerPos:FlxPoint;
var enemy:FlxSprite;
var move = false;
var progressR:FlxGroup;
var progressE:FlxGroup;
var resourceGroup:FlxGroup;
var enemyGroup:FlxGroup;
var maxItems:Int = 100;
var bar1:FlxBar;
var bartxt1:FlxText;
var bar2:FlxBar;
var bartxt2:FlxText;
var loopR:FlxAsyncLoop;
var loopE:FlxAsyncLoop;
public function addR()
{
var wood = new FlxSprite(FlxG.random.int(100, 2500), FlxG.random.int(100, 1800));
wood.makeGraphic(40, 100, FlxColor.BROWN);
add(wood);
bar1.value = (resourceGroup.members.length / maxItems) * 100;
bartxt1.text = "Loading resources... " + resourceGroup.members.length + " / " + maxItems;
bartxt1.screenCenter();
}
public function addE()
{
var enemy = new FlxSprite(FlxG.random.int(300, 2500), FlxG.random.int(300, 1800));
enemy.makeGraphic(32, 32, FlxColor.RED);
add(enemy);
bar2.value = (enemyGroup.members.length / maxItems) * 100;
bartxt2.text = "Loading enemys... " + enemyGroup.members.length + " / " + maxItems;
bartxt2.screenCenter();
}
override public function create()
{
super.create();
resourceGroup = new FlxGroup(maxItems);
enemyGroup = new FlxGroup(maxItems);
loopR = new FlxAsyncLoop(maxItems, addR);
loopE = new FlxAsyncLoop(maxItems, addE);
FlxG.camera.zoom = 0.5;
playerPos = FlxPoint.get();
ground = new FlxSprite(0, 0);
ground.makeGraphic(2500, 1800, FlxColor.GREEN);
add(ground);
player = new FlxSprite(100, 100);
player.loadGraphic(AssetPaths.n1__png);
axe = new FlxSprite(player.x + 60, player.y);
axe.loadGraphic(AssetPaths.axeR__png);
camera = new FlxCamera(0, 0, 2500, 1800);
camera.bgColor = FlxColor.TRANSPARENT;
camera.setScrollBoundsRect(0, 0, ground.width, ground.height);
FlxG.cameras.reset(camera);
camera.target = player;
if (FlxG.collide(wood, player))
FlxObject.separate(wood, player);
if (FlxG.collide(wood, enemy))
FlxObject.separate(wood, enemy);
if (FlxG.collide(enemy, player))
FlxObject.separate(enemy, player);
progressR = new FlxGroup();
bar1 = new FlxBar(0, 0, LEFT_TO_RIGHT, FlxG.width, 50, null, "", 0, 100, true);
bar1.value = 0;
bar1.screenCenter();
progressR.add(bar1);
bartxt1 = new FlxText(0, 0, FlxG.width, "Loading resources... 0 / " + maxItems);
bartxt1.setFormat(null, 28, FlxColor.WHITE, CENTER, OUTLINE);
bartxt1.screenCenter();
progressR.add(bartxt1);
progressE = new FlxGroup();
bar2 = new FlxBar(0, 0, LEFT_TO_RIGHT, FlxG.width, 50, null, "", 0, 100, true);
bar2.value = 0;
bar2.screenCenter();
progressE.add(bar2);
bartxt2 = new FlxText(0, 0, FlxG.width, "Loading enemys... 0 / " + maxItems);
bartxt2.setFormat(null, 28, FlxColor.WHITE, CENTER, OUTLINE);
bartxt2.screenCenter();
progressE.add(bartxt2);
resourceGroup.visible = false;
resourceGroup.active = false;
enemyGroup.visible = false;
enemyGroup.active = false;
add(progressR);
add(progressE);
add(resourceGroup);
add(enemyGroup);
add(loopR);
}
override public function update(elapsed:Float)
{
super.update(elapsed);
if (FlxG.keys.pressed.LEFT && move)
{
player.x -= 5;
axe.loadGraphic(AssetPaths.axeL__png);
}
if (FlxG.keys.pressed.RIGHT && move)
{
player.x += 5;
axe.loadGraphic(AssetPaths.axeR__png);
}
if (FlxG.keys.pressed.UP && move)
{
player.y -= 5;
}
if (FlxG.keys.pressed.DOWN && move)
{
player.y += 5;
}
axe.x = player.x + 60;
axe.y = player.y;
playerPos = FlxPoint.get();
playerPos = player.getMidpoint();
FlxVelocity.moveTowardsPoint(enemy, playerPos, 70);
if (!loopR.started)
{
loopR.start();
}
else
{
if (loopR.finished)
{
resourceGroup.visible = true;
progressR.visible = false;
resourceGroup.active = true;
progressR.active = false;
loopR.kill();
loopR.destroy();
add(loopE);
loopE.start();
}
}
if (loopE.finished)
{
move = true;
add(player);
add(axe);
enemyGroup.visible = true;
progressE.visible = false;
enemyGroup.active = true;
progressE.active = false;
loopE.kill();
loopE.destroy();
}
}
}
i'm showing everything because of the functions and other things that can make this problem
(my english is bad sorry if i miss something...)
Without knowing the exact error, I suspect that the problem is when you call
if (FlxG.collide(wood, player))
FlxObject.separate(wood, player);
if (FlxG.collide(wood, enemy))
FlxObject.separate(wood, enemy);
if (FlxG.collide(enemy, player))
FlxObject.separate(enemy, player);
Inside your create function - wood and enemy are declared in other functions and do not exist there.
There are a couple of things I would do differently here:
(Note: I didn't TEST this code, so there may still be problems, but this might help get you pointed in the right direction...)
class PlayState extends FlxState
{
public static var player:FlxSprite;
var ground:FlxSprite;
var axe:FlxSprite;
var base:FlxSprite;
var txt:FlxText;
var playerPos:FlxPoint;
var move:Bool = false;
var progressR:FlxGroup;
var progressE:FlxGroup;
var resourceGroup:FlxTypedGroup<FlxSprite>;
var enemyGroup:FlxTypedGroup<FlxSprite>;
var maxItems:Int = 100;
var bar1:FlxBar;
var bartxt1:FlxText;
var bar2:FlxBar;
var bartxt2:FlxText;
var loopR:FlxAsyncLoop;
var loopE:FlxAsyncLoop;
public function addR()
{
var wood = new FlxSprite(FlxG.random.int(100, 2500), FlxG.random.int(100, 1800));
wood.makeGraphic(40, 100, FlxColor.BROWN);
resourceGroup.add(wood);
bar1.value = (resourceGroup.members.length / maxItems) * 100;
bartxt1.text = "Loading resources... " + resourceGroup.members.length + " / " + maxItems;
bartxt1.screenCenter();
FlxG.collide(player, resourceGroup) // should not need to call `seperate` - it's automatic when using `collide` vs `overlap`
}
public function addE()
{
var enemy = new FlxSprite(FlxG.random.int(300, 2500), FlxG.random.int(300, 1800));
enemy.makeGraphic(32, 32, FlxColor.RED);
enemyGroup.add(enemy);
FlxG.collide(enemyGroup, resourceGroup)
FlxG.collide(player, resourceGroup)
bar2.value = (enemyGroup.members.length / maxItems) * 100;
bartxt2.text = "Loading enemys... " + enemyGroup.members.length + " / " + maxItems;
bartxt2.screenCenter();
}
override public function create()
{
super.create();
resourceGroup = new FlxTypedGroup<FlxSprite>(maxItems);
enemyGroup = new FlxTypedGroup<FlxSprite>(maxItems);
loopR = new FlxAsyncLoop(maxItems, addR);
loopE = new FlxAsyncLoop(maxItems, addE);
FlxG.camera.zoom = 0.5;
playerPos = FlxPoint.get();
ground = new FlxSprite(0, 0);
ground.makeGraphic(2500, 1800, FlxColor.GREEN);
add(ground);
player = new FlxSprite(100, 100);
player.loadGraphic(AssetPaths.n1__png);
axe = new FlxSprite(player.x + 60, player.y);
axe.loadGraphic(AssetPaths.axeR__png);
camera = new FlxCamera(0, 0, 2500, 1800);
camera.bgColor = FlxColor.TRANSPARENT;
camera.setScrollBoundsRect(0, 0, ground.width, ground.height);
FlxG.cameras.reset(camera);
camera.target = player;
progressR = new FlxGroup();
bar1 = new FlxBar(0, 0, LEFT_TO_RIGHT, FlxG.width, 50, null, "", 0, 100, true);
bar1.value = 0;
bar1.screenCenter();
progressR.add(bar1);
bartxt1 = new FlxText(0, 0, FlxG.width, "Loading resources... 0 / " + maxItems);
bartxt1.setFormat(null, 28, FlxColor.WHITE, CENTER, OUTLINE);
bartxt1.screenCenter();
progressR.add(bartxt1);
progressE = new FlxGroup();
bar2 = new FlxBar(0, 0, LEFT_TO_RIGHT, FlxG.width, 50, null, "", 0, 100, true);
bar2.value = 0;
bar2.screenCenter();
progressE.add(bar2);
bartxt2 = new FlxText(0, 0, FlxG.width, "Loading enemys... 0 / " + maxItems);
bartxt2.setFormat(null, 28, FlxColor.WHITE, CENTER, OUTLINE);
bartxt2.screenCenter();
progressE.add(bartxt2);
resourceGroup.visible = false;
resourceGroup.active = false;
enemyGroup.visible = false;
enemyGroup.active = false;
add(progressR);
add(progressE);
add(resourceGroup);
add(enemyGroup);
add(loopR);
}
public function gamePlay(elapsed:Float):Void
{
if (FlxG.keys.pressed.LEFT && move)
{
player.x -= 5; // why +/- position and not .velocity?
// THIS is probably not right - you want to use a sprite sheet with animations and call axe.play("left") or something...
axe.loadGraphic(AssetPaths.axeL__png);
}
if (FlxG.keys.pressed.RIGHT && move)
{
player.x += 5;
axe.loadGraphic(AssetPaths.axeR__png);
}
if (FlxG.keys.pressed.UP && move)
{
player.y -= 5;
}
if (FlxG.keys.pressed.DOWN && move)
{
player.y += 5;
}
axe.x = player.x + 60;
axe.y = player.y;
playerPos = FlxPoint.get();
playerPos = player.getMidpoint();
for (e in enemyGroup)
{
FlxVelocity.moveTowardsPoint(e, playerPos, 70);
}
}
public function loading(elapsed:Float):Void
{
if (!loopR.started)
{
loopR.start();
}
else if (loopR.finished)
{
resourceGroup.visible = true;
progressR.visible = false;
resourceGroup.active = true;
progressR.active = false;
loopR.kill();
loopR.destroy();
add(loopE);
loopE.start();
}
else if (loopE.finished)
{
move = true;
add(player);
add(axe);
enemyGroup.visible = true;
progressE.visible = false;
enemyGroup.active = true;
progressE.active = false;
loopE.kill();
loopE.destroy();
}
}
override public function update(elapsed:Float)
{
super.update(elapsed);
if (!move)
{
loading(elapsed);
}
else
{
gamePlay(elapsed);
}
}
}

Geometry intersection converting from direct geometry to buffergeometry

I am using Three.js. Found a really good Decal library written by Benpurdy. It's very easily modifiable and also used the techniques described here
However, the technique uses Geometry. The project I am on, uses BufferGeometry. I traced the code which does the geometry intersects and can't figure out the conversion from faces and vertices to attributes.
this.createGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.faces.length; i++){
var verts = [geom.faces[i].a, geom.faces[i].b, geom.faces[i].c];
var pts = [];
var valid = false;
for(var v = 0; v < 3; v++) {
var vec = geom.vertices[verts[v]].clone();
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
// update UV's
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = geom.faces[i].clone();
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
Appreciate if anyone could shed some light on how to go about pursuing this? Thanks.
I ended up solving the problem by writing another function to compute intersections with buffergeometry. Took me a while trying to understand the original buffer geometry code.
this.createGeometryFromBufferGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.attributes.position.array.length; i+=9){
var pts = [];
var valid = false;
for(var v = 0; v < 9; v+=3) {
var vec = new THREE.Vector3(geom.attributes.position.array[i+v],geom.attributes.position.array[i+v+1],geom.attributes.position.array[i+v+2]);
console.log((i+v) + " " + (i+v+1) + " " + (i+v+2) );
console.log(vec);
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = new THREE.Face3()
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
BufferGeometry() has a method .fromGeometry(). Populates this BufferGeometry with data from a Geometry object.
var geom = new THREE.BoxGeometry(1,1,1);
var bufGeom = new THREE.BufferGeometry().fromGeometry(geom);
UPD. You can use the other way round.
var bufGeom = new THREE.BoxBufferGeometry(1,1,1);
var geom = new THREE.Geometry().fromBufferGeometry(bufGeom);
Quick and dirty solution is to create geometry from bufferGeometry and after calculating dispose created geometry
this.compute = function()
{
this.geometry = mesh.geometry
if(this.geometry.attributes)
{
this.geometry = new THREE.Geometry().fromBufferGeometry(this.geometry);
this.computeDecal();
this.geometry.dispose();
}
else
{
this.computeDecal();
}
}

Flash AS2 - grouping enemies

So I have a Flash ActionScript 2 code, which creates a preset amount of enemies, gives enemies stats, and makes them move around randomly. Code:
//Settings
var mapWidth:Number = 550;
var mapHeight:Number = 400;
var enemiesArray:Array = new Array();
var totalEnemies:Number;
var eClip:MovieClip;
//Math functions
function getdistance(x, y, x1, y1)
{
run = x1-x;
rise = y1-y;
return (hyp(run, rise));
}
function hyp(a, b)
{
return (Math.sqrt(a*a+b*b));
}
function resetDirection(mc:MovieClip)
{
mc.roamTime = random(50);
mc.t = mc.roamTime;
mc.roamDistance = random(60)+25;
mc.randomRoamDistanceX = (Math.random()*mc.roamDistance)+mc.xx-(mc.roamDistance/2);
mc.randomRoamDistanceY = (Math.random()*mc.roamDistance)+mc.yy-(mc.roamDistance/2);
mc.newRoamDistance = getdistance(mc._x, mc._y, mc.randomRoamDistanceX, mc.randomRoamDistanceY);
mc.norm = mc.roamSpeed/mc.newRoamDistance;
mc.finalRoamDistanceX = (mc.randomRoamDistanceX-mc.xx)*mc.norm;
mc.finalRoamDistanceY = (mc.randomRoamDistanceY-mc.yy)*mc.norm;
}
//function to move enemies
function moveIt(mc:MovieClip)
{
//reduce roamTime;
mc.t--;
//move enemy to new position
if (getdistance(mc._x, mc._y, mc.randomRoamDistanceX, mc.randomRoamDistanceY)>mc.roamSpeed) {
mc._x += mc.finalRoamDistanceX;
mc._y += mc.finalRoamDistanceY;
}
//rotate enemy
XXXdiff = mc.xx-mc.randomRoamDistanceX;
YYYdiff = -(mc.yy-mc.randomRoamDistanceY);
rrradAngle = Math.atan(YYYdiff/XXXdiff);
if (XXXdiff<0) {
cccorrFactor = 270;
} else {
cccorrFactor = 90;
}
//
mc.ship_mc._rotation = -(rrradAngle*360/(2*Math.PI)+cccorrFactor);
//check if time to reset, based on roamTime
if (mc.t<=0) {
resetDirection(mc);
}
}
//
// Generate Enemies
//
// set and save enemy stats
//
//
// createEnemies(number of enemies you want, movieclip where you want to create the enemies);
//
function createEnemies(amount:Number, targetLocation:MovieClip) {
trace("createEnemies: "+amount);
for (var i = 0; i<amount; i++) {
randomXpos = Math.round(Math.random()*mapWidth);
randomYpos = Math.round(Math.random()*mapHeight);
//add new enemy to map
var newEnemy:MovieClip = targetLocation.attachMovie("enemy1", "enemy1_"+i, targetLocation.getNextHighestDepth());
enemiesArray.push(newEnemy);
//
//set enemy stats
newEnemy.id = i;
newEnemy._x = randomXpos;
newEnemy._y = randomYpos;
//save x and y position
newEnemy.xx = newEnemy._x;
newEnemy.yy = newEnemy._y;
//
newEnemy.roamSpeed = 2
newEnemy.roamTime = random(50);
newEnemy.roamDistance = random(60)+25;
newEnemy.t = 0;
//
newEnemy.myHealth = 10;
newEnemy.myName = "Small Scout";
//
resetDirection(newEnemy);
//target enemy
newEnemy.onPress = function() {
trace("Enemy: "+this.tName+" "+this.id);
target_txt.text = this.myName+": "+this.id+" Health: "+this.myHealth;
};
newEnemy.onEnterFrame = function() {
moveIt(this);
};
}
}
start_btn.onRelease = function() {
if (start_txt.text == "Start") {
//run the create enemies function to start the engine
createEnemies(box_mc.numberOfEnemies.text, map_mc);
//hide start button
start_txt._visible =false;
this._visible = false;
box_mc._visible = false;
}
};
I want program enemies to be grouped (based on fireflies algorithm). My idea is write for loop to define attractiveness, but I don't know how to make my objects move to the most attractiveness. Maybe someone would help me with this problem?
I change this line:
newEnemy.myHealth = 10;
on this
newEnemy.myHealth = Math.round(random(9)+1);
myHealth would be responsible for attractiveness. I try to use code from this site and modificate code to let objects with low attractiveness follow objects with large attractiveness. Also, I want to stop algorith, when they are in the groups.

Resources