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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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]); | |
} | |
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:
- Calculate the total length of the pillar + in between tiles section and check if it fits onto the building side
- Calculate the number of tiles that fit on each side of the pillar section (needs to be an even number)
- 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:
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 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.
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.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
- Fix Import and Read .json file python code
- Add Ground Floor Section
- Figure out Material IDs !!
- LODs
- Collision
Nice!
ReplyDeleteSo 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