Wednesday, 12 February 2020

Week 5 and 6.1 - Sorting the Building Middle Floor Section

After thinking I had sorted the .json files  I found out the hard way this week, that once I converted my nodes into an HDA, my python code stopped working, so I guess this will go back onto my To-Do list.
On the bright sight I made quite a bit of progress with the building construction itself and got the middle floor section into a working state.

1 - Corners

First I went back into the Corner Node, cleaned up the code a bit and got rid of a bug, where depending on where point 0 was, it would not get assigned an instance attribute, because some of the angles where messed up. I fixed this by appending point 0 to the point array and working with that instead of always adding a special case for the last point of the curve.


#include <math.h>
//VARIABLES
int points[] = primpoints(geoself(), 0);
int numCorners = len(points);
append(points, 0);
vector pointPos[];
vector edgeDir[];
float cornerAngles[];
//get available corner pieces
string corner_90_param = chs("../kit_storage/mfloor_kit_corner_90");
string corner_90_pieces[] = split(corner_90_param);
string corner_45_param = chs("../kit_storage/mfloor_kit_corner_45");
string corner_45_pieces[] = split(corner_45_param);
//set corner pieces
int cornerIndex = (chi('../../mfloor_corner_index')) % len(corner_90_pieces);
string default_corner_45 = corner_45_pieces[cornerIndex];
string default_corner_90 = corner_90_pieces[cornerIndex];
float wallHeight = getbbox_size(chs("../../" + default_corner_90 + "/" + default_corner_90 + "/file"))[1];
//get point positions
for(int i = 0; i < len(points); i++) {
vector newPos = pointattrib(geoself(), "P", points[i], 1);
pointPos[i] = newPos;
setpointattrib(geoself(), "scale", i, set(1,1,1), "set");
//printf('%g: %e \n', points[i], pointPos[i]);
}
//Direction Vectors between Points
for(int i = 0; i < len(points)-1; i++) {
vector dir = normalize(pointPos[i+1]-pointPos[i]);
edgeDir[i] = dir;
//printf('%g - %g, Vector: %e \n', points[i], points[i+1], edgeDir[i]);
}
append(edgeDir, edgeDir[0]);
//get angles
for(int i = 0; i < len(edgeDir) - 1; i++){
int ptIndex = (i+1) % numCorners;
vector v1 = set(edgeDir[i][0], 0, edgeDir[i][2]);
vector v2 = set(edgeDir[i + 1][0], 0, edgeDir[i + 1][2]);
float angle = atan2(v2[2], v2[0])-atan2(v1[2], v1[0]);
if(angle > PI) {
angle -= 2 * PI;
}
else if(angle <= PI * -1) {
angle += 2 * PI;
}
angle = degrees(angle);
vector n;
//90 degree corners
if(abs(rint(angle)) >= 89 && abs(rint(angle)) <= 91){
n = v2 * -1;
if(angle < 0) {
float theta = radians(-90);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
setpointattrib(geoself(), "N", ptIndex, n, "set");
setattrib(geoself(), "point", "instance", ptIndex, 0, "../../" + default_corner_90, "set");
vector dim_max = getbbox_max(chs("../../" + default_corner_90 + "/" + default_corner_90 + "/file"));
float x_max = dim_max[0];
setattrib(geoself(), "point", "piece_x_size", ptIndex, 0, x_max, "set");
cornerAngles[ptIndex] = 90;
}
//45 degree corners
else if(abs(rint(angle)) >= 44 && abs(rint(angle)) <= 46){
n = v2 * -1;
if(angle < 0) {
float theta = radians(-90);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
else {
float theta = radians(45);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
setpointattrib(geoself(), "N", ptIndex, n, "set");
setattrib(geoself(), "point", "instance", ptIndex, 0, "../../" + default_corner_45, "set");
vector dim_max = getbbox_max(chs("../../" + default_corner_45 + "/" + default_corner_45 + "/file"));
float x_max = dim_max[0];
setattrib(geoself(), "point", "piece_x_size", ptIndex, 0, x_max, "set");
cornerAngles[ptIndex] = 45;
}
printf("%g: %f \n",(i+1) % numCorners, cornerAngles[ptIndex]);
}
This is of course only working for one floor, so I adapted it later, when working on the floor separation. Also I added a parameter for selecting the corner pieces.


2 - Wall Sections

Going back to the wall sections I basically got rid of all my previous code for them. As I stated in a previous blog post, we didn't want to have any scaled tiles, except plain wall tiles, where scaling doesn't matter.
However, since Litha added pillars to the modular kit and wanted to have parameters for 'Number of pillars' per building side and 'Wall Tiles in between Pillars' I had to change the way of how I calculated the tile positioning.
I ended up breaking this down into the following steps:

  1. Calculate the total length of the pillar + in between tiles section and check if it fits onto the building side
  2. Calculate the number of tiles that fit on each side of the pillar section (needs to be an even number)
  3. Fill the rest of the wall length with scaled plain wall tiles.
We agreed to have the pillar section in the centre of the building side for now. Of course it would be possible to add the option of offsetting the section, but  we'll have to see, if it is necessary.

The code bit for this looks like this, again this version only working for one floor, but I will show the multiple floor version later:


//VARIABLES
int points[] = primpoints(geoself(), 0);
append(points, points[0]);
int numPt = len(points);
//modular kit piece arrays
//pillars
string pillars = chs('../kit_storage/mfloor_kit_pillar');
string pillarArray[] = split(pillars);
int pillarIndex = int(chi('../../mfloor_pillar_index')%len(pillarArray));
string pillarRef = '../../'+ pillarArray[pillarIndex];
int numPillars = chi('../../num_pillars');
float pillarWidth = getbbox_size(chs('../../' + pillarArray[0] + '/' + pillarArray[0] + '/file' ))[0];
//plain wall
string wall = chs('../kit_storage/mfloor_kit_wall');
string wallRef = '../../'+ wall;
//wall pieces
string wallPieces = chs('../kit_storage/mfloor_kit_ww');
string wallPieceArray[] = split(wallPieces);
float wallWidth = getbbox_size(chs('../../' + wall + '/' + wall + '/file' ))[0];
int numWallPieces = chi('../../num_pieces_btwn_pillars');
//windows
string windows = chs('../kit_storage/mfloor_kit_window');
string windowArray[] = split(windows);
string windowsRd[], windowsSq[];
//sort windows
for(int w = 0; w < len(windowArray); w++){
if ( match('*rd*', tolower(windowArray[w])) == 1){
append(windowsRd, windowArray[w]);
}
else if ( match('*sq*', tolower(windowArray[w])) == 1){
append(windowsSq, windowArray[w]);
}
}
//Function for adding windows to the Window Frames
void addWindows (string wRd[]; string wSq[]; string wPiece; vector wNormal; vector wPos) {
int wIndex = chi('../../mfloor_window_index') % len(wRd);
int wPt;
string wRef;
if (match('*rd*', tolower(wPiece))) {
wRef = '../../' + wRd[wIndex];
wPt = addpoint(geoself(), wPos);
setpointattrib(geoself(), 'instance', wPt, wRef, 'set');
setpointattrib(geoself(), 'scale', wPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', wPt, wNormal, 'set');
}
else if (match('*sq*', tolower(wPiece))){
wRef = '../../' + wSq[wIndex];
wPt = addpoint(geoself(), wPos);
setpointattrib(geoself(), 'instance', wPt, wRef, 'set');
setpointattrib(geoself(), 'scale', wPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', wPt, wNormal, 'set');
}
}
//PLACE WALL SECTIONS
for(int i = 0; i < numPt - 1; i++){
//PREP
// start and end point position
vector startPointPos = pointattrib(geoself(), 'P', points[i], 1);
vector endPointPos = pointattrib(geoself(), 'P', points[i + 1], 1);
vector dir = normalize(endPointPos - startPointPos);
// start and end position, taking corner piece width into consideration
vector startPos = startPointPos + (pointattrib(geoself(), 'piece_x_size', points[i], 1)*dir);
vector endPos = endPointPos - (pointattrib(geoself(), 'piece_x_size', points[i + 1], 1)*dir);
//normal for placing the pieces on the edges
vector n = set(dir[2], 0, dir[0] * -1);
float dist = distance(startPos, endPos);
//only place tiles, if corner pieces don't overlap
if ( (pointattrib(geoself(), 'piece_x_size', points[i], 1) + pointattrib(geoself(), 'piece_x_size', points[i + 1], 1)) < distance(startPointPos, endPointPos) ) {
//CALCULATE SECTIONS
float sectionWidth = (numWallPieces * wallWidth) * clamp(numPillars - 1, 0, 10) + numPillars * pillarWidth;
sectionWidth *= int(sectionWidth < dist);
int numSections;
numSections = int((sectionWidth != 0));
numWallPieces *= int(numPillars > 1);
//num tiles on each side of pillar section
float restWidth = dist - sectionWidth;
int numPieces;
if (sectionWidth != 0) {
numPieces = int(floor((restWidth/2)/wallWidth));
numPieces -= (numPieces % 2) * (1 - int(sectionWidth < dist));
}
else {
numPieces = int(floor(restWidth / wallWidth));
}
//filler
float fillerWidth;
if (sectionWidth != 0) {
fillerWidth = (restWidth - ((numPieces * 2) * wallWidth))/2;
}
else {
fillerWidth = (restWidth - ((numPieces) * wallWidth))/2;
}
float fillerScaleX = fillerWidth / wallWidth;
//PLACE PIECES
//place filler pieces
vector tempPos;
vector tempStartPos;
vector tempEndPos;
vector pillarStartPos;
int tempPt;
int pieceIndex = chi('../../mfloor_wframe_index') % len(wallPieceArray);
string piece = wallPieceArray[pieceIndex];
string pieceRef = '../../'+ piece;
if(fillerWidth != 0) {
if(dist > wallWidth) {
tempPos = startPos + ((fillerWidth/2) * dir);
//Start Filler
tempPt = addpoint(geoself(), tempPos);
tempStartPos = tempPos;
setpointattrib(geoself(), 'instance', tempPt, wallRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
//End Pos
tempPt = addpoint(geoself(), endPos - ((fillerWidth/2) * dir));
tempEndPos = endPos - ((fillerWidth/2) * dir);
setpointattrib(geoself(), 'instance', tempPt, wallRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
}
else {
tempPos = startPos + (fillerWidth * dir);
tempPos = startPos + (fillerWidth * dir);
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, wallRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX * 2,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
}
}
//place outside wall tiles
if (numPieces != 0){
if(sectionWidth != 0){
for (int p = 0; p < numPieces; p++){
//place a tile both at the start and end of the wall
tempPos = tempStartPos + (wallWidth * dir)/2 + (wallWidth * dir) * p + (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, pieceRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
if (p == numPieces - 1) {
pillarStartPos = tempPos;
}
addWindows(windowsRd, windowsSq, piece, n, tempPos);
tempPos = tempEndPos - (wallWidth * dir)/2 - (wallWidth * dir) * p - (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, pieceRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
addWindows(windowsRd, windowsSq, piece, n, tempPos);
}
}
else {
for (int p = 0; p < numPieces; p++) {
tempPos = tempStartPos + (wallWidth * dir)/2 + (wallWidth * dir) * p + (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, pieceRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
addWindows(windowsRd, windowsSq, piece, n, tempPos);
}
}
}
//if no wall tiles get placed
else {
pillarStartPos = tempPos + (fillerWidth * dir)/2;
}
//place pillar sections
if(numSections != 0){
printf('Pillar Start Pos: %e \n', pillarStartPos);
printf('NumPieces: %g \n', numPieces);
for (int pl = 0; pl < numPillars; pl++){
vector pillarPos;
if(numPieces != 0){
pillarPos = (pillarStartPos + (wallWidth * dir)/2 + (pillarWidth * dir)/2 + (pillarWidth * dir) * pl) + ((numWallPieces * wallWidth)*dir)*pl;
}
else {
pillarPos = (pillarStartPos + (pillarWidth * dir)/2 + (pillarWidth * dir) * pl) + ((numWallPieces * wallWidth)*dir)*pl;
}
tempPt = addpoint(geoself(), pillarPos);
setpointattrib(geoself(), 'instance', tempPt, pillarRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
for (int sWall = 0; sWall < numWallPieces; sWall++){
if(pl < numPillars - 1){
vector wallPos = pillarPos + (pillarWidth * dir)/2 + (wallWidth * dir)/2 + (wallWidth * dir)*sWall;
tempPt = addpoint(geoself(), wallPos);
setpointattrib(geoself(), 'instance', tempPt, pieceRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
addWindows(windowsRd, windowsSq, piece, n, wallPos);
}
}
}
}
}
}
I originally also thought about somehow calculating the maximum amount of pillars, with the given number of in between pieces, but for now I left it to just fill the wall with tiles, if the specified pillar amount doesn't fit.
I also, similar to the corner pieces, added Parameters to switch through the window frame, window, and pillar meshes.


3 - Floor and Roof Separation

After having figured out the basics for the corners and walls, I moved on to implementing multiple floors, including the option to either add separation pieces to every floor, or to add them only to specific floors.
For the top floor it automatically uses the roof separation tiles, instead of the floor separation ones.

When adding the corners to each row of the building, I also added an attribute to the points, depending on whether it was a wall, floor separation or roof separation row, so I could add the corresponding tiles, when adding the wall sections.

Here are the updated code bits for corners and walls.

#include <math.h>
//VARIABLES
int points[] = primpoints(geoself(), 0);
int numCorners = len(points);
append(points, 0);
vector pointPos[];
vector edgeDir[];
float cornerAngles[];
vector cornerNormals[];
float cornerXSizes[];
//FLOORS AND SEPARATION LISTS
int numFloors = chi('../../numFloors');
// Add Separation after each Floor
int sepForEachFloor = chi('../../chk_floor_sep');
// Add Separation --> only applicable, if sepForEachFloor == 0
int numFloorSep = chi('../../mp_separation');
int floorSepIndices[];
for (int i = 1; i <= numFloorSep; i++){
int floorIndex = chi('../../sep_floor_num'+itoa(i));
int inArray = find(floorSepIndices, floorIndex);
printf('%g is in Array: %g \n', floorIndex, inArray);
if (inArray < 0) {
append(floorSepIndices, floorIndex);
}
}
//MFLOOR CORNER PIECES
string corner_90_param = chs("../kit_storage/mfloor_kit_corner_90");
string corner_90_pieces[] = split(corner_90_param);
string corner_45_param = chs("../kit_storage/mfloor_kit_corner_45");
string corner_45_pieces[] = split(corner_45_param);
//set corner pieces
int cornerIndex = (chi('../../mfloor_corner_index')) % len(corner_90_pieces);
string default_corner_45 = corner_45_pieces[cornerIndex];
string default_corner_90 = corner_90_pieces[cornerIndex];
float wallHeight = getbbox_size(chs("../../" + default_corner_90 + "/" + default_corner_90 + "/file"))[1];
//SEPARATION CORNER PIECES
//fsep
string fsep_corner_45 = chs('../kit_storage/fsep_kit_corner_45');
string fsep_corner_90 = chs('../kit_storage/fsep_kit_corner_90');
string fsep_45_array[] = split(fsep_corner_45);
string fsep_90_array[] = split(fsep_corner_90);
//rsep
string rsep_corner_45 = chs('../kit_storage/rsep_kit_corner_45');
string rsep_corner_90 = chs('../kit_storage/rsep_kit_corner_90');
string rsep_45_array[] = split(rsep_corner_45);
string rsep_90_array[] = split(rsep_corner_90);
//set fsep corner pieces
int fSepIndex = (chi('../../fsep_index')) % len(fsep_90_array);
int rSepIndex = (chi('../../rsep_index')) % len(rsep_90_array);
string fsep_45 = fsep_45_array[fSepIndex];
string fsep_90 = fsep_90_array[fSepIndex];
string rsep_45 = rsep_45_array[rSepIndex];
string rsep_90 = rsep_90_array[rSepIndex];
float fSepHeight = getbbox_size(chs("../../" + fsep_90 + "/" + fsep_90 + "/file"))[1];
float rSepHeight = getbbox_size(chs("../../" + rsep_90 + "/" + rsep_90 + "/file"))[1];
float sepHeight = fSepHeight;
//rsep
//get point positions
for(int i = 0; i < len(points); i++) {
vector newPos = pointattrib(geoself(), "P", points[i], 1);
pointPos[i] = newPos;
//printf('%g: %e \n', points[i], pointPos[i]);
}
//Direction Vectors between Points
for(int i = 0; i < len(points)-1; i++) {
vector dir = normalize(pointPos[i+1]-pointPos[i]);
edgeDir[i] = dir;
//printf('%g - %g, Vector: %e \n', points[i], points[i+1], edgeDir[i]);
}
append(edgeDir, edgeDir[0]);
//get angles
for(int i = 0; i < len(edgeDir) - 1; i++){
int ptIndex = (i+1) % numCorners;
vector v1 = set(edgeDir[i][0], 0, edgeDir[i][2]);
vector v2 = set(edgeDir[i + 1][0], 0, edgeDir[i + 1][2]);
float angle = atan2(v2[2], v2[0])-atan2(v1[2], v1[0]);
if(angle > PI) {
angle -= 2 * PI;
}
else if(angle <= PI * -1) {
angle += 2 * PI;
}
angle = degrees(angle);
vector n;
//90 degree corners
if(abs(rint(angle)) >= 89 && abs(rint(angle)) <= 91){
n = v2 * -1;
if(angle < 0) {
float theta = radians(-90);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
setpointattrib(geoself(), "N", ptIndex, n, "set");
vector dim_max = getbbox_max(chs("../../" + default_corner_90 + "/" + default_corner_90 + "/file"));
float x_max = dim_max[0];
setattrib(geoself(), "point", "piece_x_size", ptIndex, 0, x_max, "set");
cornerXSizes[ptIndex] = x_max;
cornerAngles[ptIndex] = 90;
}
//45 degree corners
else if(abs(rint(angle)) >= 44 && abs(rint(angle)) <= 46){
n = v2 * -1;
if(angle < 0) {
float theta = radians(-90);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
else {
float theta = radians(45);
float cs = cos(theta);
float sn = sin(theta);
float px = n[0] * cs - n[2] * sn;
float pz = n[0] * sn + n[2] * cs;
n = set(px, n[1], pz);
}
setpointattrib(geoself(), "N", ptIndex, n, "set");
vector dim_max = getbbox_max(chs("../../" + default_corner_45 + "/" + default_corner_45 + "/file"));
float x_max = dim_max[0];
setattrib(geoself(), "point", "piece_x_size", ptIndex, 0, x_max, "set");
cornerXSizes[ptIndex] = x_max;
cornerAngles[ptIndex] = 45;
}
cornerNormals[ptIndex] = n;
}
// vector dim_max = getbbox_max(chs("../../" + default_corner_90 + "/" + default_corner_90 + "/file"));
// float x_max = dim_max[0];
// setattrib(geoself(), "point", "piece_x_size", ptIndex, 0, x_max, "set");
if(numFloors >= 1) {
float heightOffset = 0;
int useSep = 0;
//loop through floors
for (int f = 1; f <= numFloors; f++) {
string sep_type = 'floor_sep';
string sep_45 = fsep_45;
string sep_90 = fsep_90;
sepHeight = fSepHeight;
if(f == numFloors) {
sep_type = 'roof_sep';
sep_45 = rsep_45;
sep_90 = rsep_90;
sepHeight = rSepHeight;
}
if( f == 1 ) {
for ( int c = 0; c < numCorners; c++ ){
if (rint(cornerAngles[c]) == 90){
setpointattrib(geoself(), "instance", c, "../../" + default_corner_90, "set");
setpointattrib(geoself(), "row_type", c, 'floor_wall', "set");
}
else if (rint(cornerAngles[c]) == 45){
setpointattrib(geoself(), "instance", c, "../../" + default_corner_45, "set");
setpointattrib(geoself(), "row_type", c, 'floor_wall', "set");
}
}
//separation
if ( sepForEachFloor || (find(floorSepIndices, f) >= 0)) {
useSep = 1;
for ( int c = 0; c < numCorners; c++ ) {
vector tempPos = pointattrib(geoself(), 'P', points[c], 0);
tempPos = set(tempPos[0], wallHeight, tempPos[2]);
int pt = addpoint(geoself(), tempPos);
if (rint(cornerAngles[c]) == 90){
float dim = getbbox_max(chs("../../" + sep_90 + "/" + sep_90 + "/file"))[0];
setpointattrib(geoself(), "instance", pt, "../../" + sep_90, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, dim, "set");
setpointattrib(geoself(), "row_type", pt, sep_type, "set");
}
else if (rint(cornerAngles[c]) == 45){
float dim = getbbox_max(chs("../../" + sep_45 + "/" + sep_45 + "/file"))[0];
setpointattrib(geoself(), "instance", pt, "../../" + sep_45, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, dim, "set");
setpointattrib(geoself(), "row_type", pt, sep_type, "set");
}
}
}
else {
useSep = 0;
}
heightOffset += wallHeight + sepHeight * useSep;
}
else if ( f > 1 ) {
for ( int c = 0; c < numCorners; c++ ){
vector tempPos = pointattrib(geoself(), 'P', points[c], 0);
tempPos = set(tempPos[0], heightOffset, tempPos[2]);
int pt = addpoint(geoself(), tempPos);
if (rint(cornerAngles[c]) == 90){
setpointattrib(geoself(), "instance", pt, "../../" + default_corner_90, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, cornerXSizes[c], "set");
setpointattrib(geoself(), "row_type", pt, 'floor_wall', "set");
}
else if (rint(cornerAngles[c]) == 45){
setpointattrib(geoself(), "instance", pt, "../../" + default_corner_45, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, cornerXSizes[c], "set");
setpointattrib(geoself(), "row_type", pt, 'floor_wall', "set");
}
}
//separation
if ( sepForEachFloor || (find(floorSepIndices, f) >= 0)) {
useSep = 1;
for ( int c = 0; c < numCorners; c++ ) {
vector tempPos = pointattrib(geoself(), 'P', points[c], 0);
tempPos = set(tempPos[0], heightOffset + wallHeight, tempPos[2]);
int pt = addpoint(geoself(), tempPos);
if (rint(cornerAngles[c]) == 90){
float dim = getbbox_max(chs("../../" + sep_90 + "/" + sep_90 + "/file"))[0];
setpointattrib(geoself(), "instance", pt, "../../" + sep_90, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, dim, "set");
setpointattrib(geoself(), "row_type", pt, sep_type, "set");
}
else if (rint(cornerAngles[c]) == 45){
float dim = getbbox_max(chs("../../" + sep_45 + "/" + sep_45 + "/file"))[0];
setpointattrib(geoself(), "instance", pt, "../../" + sep_45, "set");
setpointattrib(geoself(), "N", pt, cornerNormals[c], "set");
setpointattrib(geoself(), "scale", pt, set(1,1,1), "set");
setpointattrib(geoself(), "piece_x_size", pt, dim, "set");
setpointattrib(geoself(), "row_type", pt, sep_type, "set");
}
}
}
else {
useSep = 0;
}
heightOffset += wallHeight + sepHeight * useSep;
}
useSep = 0;
}
}

//VARIABLES
int points[] = primpoints(geoself(), 0);
int numPtFloor = len(points);
int numPt = @numpt;
int numRows = numPt / numPtFloor;
//modular kit piece arrays
//pillars
string pillars = chs('../kit_storage/mfloor_kit_pillar');
string pillarArray[] = split(pillars);
int pillarIndex = int(chi('../../mfloor_pillar_index')%len(pillarArray));
string pillarRef = '../../'+ pillarArray[pillarIndex];
int numPillars = chi('../../num_pillars');
float pillarWidth = getbbox_size(chs('../../' + pillarArray[0] + '/' + pillarArray[0] + '/file' ))[0];
//plain wall
string wall = chs('../kit_storage/mfloor_kit_wall');
string wallRef = '../../'+ wall;
float wallWidth = getbbox_size(chs('../../' + wall + '/' + wall + '/file' ))[0];
//wall pieces
string wallPieces = chs('../kit_storage/mfloor_kit_ww');
string wallPieceArray[] = split(wallPieces);
int pieceIndex = chi('../../mfloor_wframe_index') % len(wallPieceArray);
string piece = wallPieceArray[pieceIndex];
string pieceRef = '../../'+ piece;
int numWallPieces = chi('../../num_pieces_btwn_pillars');
//windows
string windows = chs('../kit_storage/mfloor_kit_window');
string windowArray[] = split(windows);
string windowsRd[], windowsSq[];
//sort windows
for(int w = 0; w < len(windowArray); w++){
if ( match('*rd*', tolower(windowArray[w])) == 1){
append(windowsRd, windowArray[w]);
}
else if ( match('*sq*', tolower(windowArray[w])) == 1){
append(windowsSq, windowArray[w]);
}
}
// floor separation
string fSep = chs('../kit_storage/fsep_kit_wall');
string fSepPillar = chs('../kit_storage/fsep_kit_pillar');
string fSepArray[] = split(fSep);
string fSepPillarArray[] = split(fSepPillar);
string fSepRef = '../../' + fSepArray[0];
string fSepPillarRef = '../../' + fSepPillarArray[0];
// roof separation
string rSep = chs('../kit_storage/rsep_kit_wall');
string rSepArray[] = split(rSep);
int rSepIndex = chi('../../rsep_index') % len(rSepArray);
string rSepRef = '../../' + rSepArray[rSepIndex];
//Function for adding windows to the Window Frames
void addWindows (string wRd[]; string wSq[]; string wPiece; vector wNormal; vector wPos) {
int wIndex = chi('../../mfloor_window_index') % len(wRd);
int wPt;
string wRef;
if (match('*rd*', tolower(wPiece))) {
wRef = '../../' + wRd[wIndex];
wPt = addpoint(geoself(), wPos);
setpointattrib(geoself(), 'instance', wPt, wRef, 'set');
setpointattrib(geoself(), 'scale', wPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', wPt, wNormal, 'set');
}
else if (match('*sq*', tolower(wPiece))){
wRef = '../../' + wSq[wIndex];
wPt = addpoint(geoself(), wPos);
setpointattrib(geoself(), 'instance', wPt, wRef, 'set');
setpointattrib(geoself(), 'scale', wPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', wPt, wNormal, 'set');
}
}
int numFloors = chi('../../numFloors');
//PLACE WALL SECTIONS
if (numFloors > 0) {
for (int r = 0; r < numRows; r++) { //loop through wall and separation rows
//get points belonging to each row
int rowPoints[] = {};
int start = r * numPtFloor;
for ( int c = 0; c < numPtFloor; c++ ) {
append(rowPoints, start + c);
}
append(rowPoints, start);
string rowType = pointattrib(geoself(), 'row_type', rowPoints[0], 1);
//place wall tiles
for (int c = 0; c < len(rowPoints)-1; c++) { //loop through corner points
//PREP
// start and end point position
vector startPointPos = pointattrib(geoself(), 'P', rowPoints[c], 1);
vector endPointPos = pointattrib(geoself(), 'P', rowPoints[c + 1], 1);
vector dir = normalize(endPointPos - startPointPos);
// start and end position, taking corner piece width into consideration
vector startPos = startPointPos + (pointattrib(geoself(), 'piece_x_size', rowPoints[c], 1)*dir);
vector endPos = endPointPos - (pointattrib(geoself(), 'piece_x_size', rowPoints[c + 1], 1)*dir);
//normal for placing the pieces on the edges
vector n = set(dir[2], 0, dir[0] * -1);
float dist = distance(startPos, endPos);
//only place tiles, if corner pieces don't overlap
if ( (pointattrib(geoself(), 'piece_x_size', rowPoints[c], 1) + pointattrib(geoself(), 'piece_x_size', rowPoints[c + 1], 1)) < distance(startPointPos, endPointPos) ) {
//CALCULATE SECTIONS
float sectionWidth = (numWallPieces * wallWidth) * clamp(numPillars - 1, 0, 10) + numPillars * pillarWidth;
sectionWidth *= int(sectionWidth < dist);
int numSections;
numSections = int((sectionWidth != 0));
numWallPieces *= int(numPillars > 1);
//num tiles on each side of pillar section
float restWidth = dist - sectionWidth;
int numPieces;
if (sectionWidth != 0) {
numPieces = int(floor((restWidth/2)/wallWidth));
numPieces -= (numPieces % 2) * (1 - int(sectionWidth < dist));
}
else {
numPieces = int(floor(restWidth / wallWidth));
}
//filler
float fillerWidth;
if (sectionWidth != 0) {
fillerWidth = (restWidth - ((numPieces * 2) * wallWidth))/2;
}
else {
fillerWidth = (restWidth - ((numPieces) * wallWidth))/2;
}
float fillerScaleX = fillerWidth / wallWidth;
//PLACE PIECES
//place filler pieces
vector tempPos;
vector tempStartPos;
vector tempEndPos;
vector pillarStartPos;
int tempPt;
if (fillerWidth != 0) {
string fillerRef = wallRef;
if (rowType == 'floor_wall'){
fillerRef = wallRef;
}
else if (rowType == 'floor_sep'){
fillerRef = fSepRef;
}
else if (rowType == 'roof_sep'){
fillerRef = rSepRef;
}
if(dist > wallWidth) {
tempPos = startPos + ((fillerWidth/2) * dir);
//Start Filler
tempPt = addpoint(geoself(), tempPos);
tempStartPos = tempPos;
setpointattrib(geoself(), 'instance', tempPt, fillerRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
//End Pos
tempPt = addpoint(geoself(), endPos - ((fillerWidth/2) * dir));
tempEndPos = endPos - ((fillerWidth/2) * dir);
setpointattrib(geoself(), 'instance', tempPt, fillerRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
}
else {
tempPos = startPos + (fillerWidth * dir);
tempPos = startPos + (fillerWidth * dir);
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, fillerRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(fillerScaleX * 2,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
}
}
//place outside wall tiles
if (numPieces != 0){
string spareRef = pieceRef;
if (rowType == 'floor_wall'){
spareRef = pieceRef;
}
else if (rowType == 'floor_sep'){
spareRef = fSepRef;
}
else if (rowType == 'roof_sep'){
spareRef = rSepRef;
}
if(sectionWidth != 0){
for (int p = 0; p < numPieces; p++){
//place a tile both at the start and end of the wall
tempPos = tempStartPos + (wallWidth * dir)/2 + (wallWidth * dir) * p + (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, spareRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
if (p == numPieces - 1) {
pillarStartPos = tempPos;
}
if ( rowType == 'floor_wall') {
addWindows(windowsRd, windowsSq, piece, n, tempPos);
}
tempPos = tempEndPos - (wallWidth * dir)/2 - (wallWidth * dir) * p - (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, spareRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
if ( rowType == 'floor_wall') {
addWindows(windowsRd, windowsSq, piece, n, tempPos);
}
}
}
else {
for (int p = 0; p < numPieces; p++) {
tempPos = tempStartPos + (wallWidth * dir)/2 + (wallWidth * dir) * p + (fillerWidth * dir)/2;
tempPt = addpoint(geoself(), tempPos);
setpointattrib(geoself(), 'instance', tempPt, spareRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
if ( rowType == 'floor_wall') {
addWindows(windowsRd, windowsSq, piece, n, tempPos);
}
}
}
}
//if no wall tiles get placed
else {
pillarStartPos = tempPos + (fillerWidth * dir)/2;
}
//place pillar sections
if(numSections != 0){
string wpRef = pieceRef;
string pRef = pillarRef;
float xScale = 1;
if (rowType == 'floor_wall'){
wpRef = pieceRef;
pRef = pillarRef;
xScale = 1;
}
else if (rowType == 'floor_sep'){
wpRef = fSepRef;
pRef = fSepPillarRef;
xScale = 1;
}
else if (rowType == 'roof_sep'){
wpRef = rSepRef;
pRef = rSepRef;
xScale = pillarWidth / wallWidth;
}
printf('Pillar Start Pos: %e \n', pillarStartPos);
printf('NumPieces: %g \n', numPieces);
for (int pl = 0; pl < numPillars; pl++){
vector pillarPos;
if(numPieces != 0){
pillarPos = (pillarStartPos + (wallWidth * dir)/2 + (pillarWidth * dir)/2 + (pillarWidth * dir) * pl) + ((numWallPieces * wallWidth)*dir)*pl;
}
else {
pillarPos = (pillarStartPos + (pillarWidth * dir)/2 + (pillarWidth * dir) * pl) + ((numWallPieces * wallWidth)*dir)*pl;
}
tempPt = addpoint(geoself(), pillarPos);
setpointattrib(geoself(), 'instance', tempPt, pRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(xScale,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
for (int sWall = 0; sWall < numWallPieces; sWall++){
if(pl < numPillars - 1){
vector wallPos = pillarPos + (pillarWidth * dir)/2 + (wallWidth * dir)/2 + (wallWidth * dir) * sWall;
tempPt = addpoint(geoself(), wallPos);
setpointattrib(geoself(), 'instance', tempPt, wpRef, 'set');
setpointattrib(geoself(), 'scale', tempPt, set(1,1,1), 'set');
setpointattrib(geoself(), 'N', tempPt, n, 'set');
if (rowType == 'floor_wall') {
addWindows(windowsRd, windowsSq, piece, n, wallPos);
}
}
}
}
}
} //end if
} // end for c
} // end for r
}


I also brought the HDA into Unreal and it seems to be working alright, but we really need to figure out LODs, otherwise we sacrifice performance, where it is not necessary.


4 - Roof Mesh

Since the roof at that point was just a hole and might have caused issues with lighting (leaving aside, that in a game the rooftop might be visible), I needed to create a mesh from the footprint curve, which I had to do in a separate geometry node. Since Mike explained the use and advantages of detail attributes to me, I thought I could store the point positions of the curve in a detail attribute and use it to recreate the curve. I had some trouble figuring out how to get the attribute reference work with the coordinate parameter of the new curve, so I just used python, modified the attribute to fit the pattern of the coordinates and it ended up working.

node = hou.pwd()
geo = node.geometry()
# Add code to modify contents of geo.
# Use drop down menu to select examples.
nGeo = hou.node('../../Instance/output0/').geometry()
#print(nGeo.findGlobalAttrib('mfloor_curve_points'))
#print(nGeo.attribValue('mfloor_curve_points'))
curvePoints = nGeo.floatListAttribValue('mfloor_curve_points')
mfloorHeight = nGeo.floatAttribValue('mfloor_height_total')
print mfloorHeight
def listChunks(l, n):
for i in range(0, len(l), n):
yield l[i: i+n]
pointList = list(listChunks(curvePoints, 3))
coordinateString = ''
for i in pointList:
coordinateString += str(i)[1:-1] + ' '
curveNode = hou.node('../mfloor_roof')
curveNode.parm('coords').set(coordinateString)
transformNode = hou.node('../transform_height_offset')
transformNode.parm('ty').set(mfloorHeight)

After using a divide, poly-extrude and unwrap node, I ended up with a pretty usable roof mesh. I also stored the height of the mid floor section in a detail attribute and used it to offset it.

Complete Middle Floor Section

5 - To Do

  1. Fix Import and Read .json file python code
  2. Add Ground Floor Section
  3. Figure out Material IDs !!
  4. LODs
  5. Collision

1 comment:

  1. Nice!

    So is this making one big piece of unique geometry? or is it placing the tile instances into unreal as a bunch of static meshes? The latter is more complex, but would save on geometry memory and permit culling of parts of the building which could help render perf.

    Regarding LODs, consider whether you will have a unique normal texture for each tile. If yes, I recommend to point the vertex normals of the LOD0 all in the same direction, away from the building tile before baking the normal map. It will look very strange to have no shading on the LOD0 until the normal is on, and the normal might look very strong, but run with it... the advantage of this is it greatly reduces the visual pop you get when transitioning to a flat plane (or similar) LOD. Feel free to get in touch if that doesn't make sense.

    Also, I notice you have a lot of your logic in VEX. That's great, but it might be worth setting yourself a bit of a challenge to see what you can do with the houdini nodes for some of this. The main advantage here is it makes debugging a littel simpler, because you can view the geometry at a certain node and examine the geometry spreadsheet.

    Cool stuff, keep it up! :D

    ReplyDelete