bool InstanceSurfaceScatterBuilder::testPositionPickAndAddObject(Scene& scene, const Point& position, RNG& rng, TestPosInfo& posInfo) { Ray occlusionRay(position, Normal(0.0f, -1.0f, 0.0f), RAY_ALL); // BVH intersectors depend on signed inf values resulting from div-by-0 for axis-aligned rays... occlusionRay.calculateInverseDirection(); HitResult hitResult; float t = 1000.0f; if (scene.didHitObject(occlusionRay, t, hitResult)) { if (hitResult.hitPoint.y < m_minimumCutoff) return false; Object* pSrcObject = posInfo.pCurrentSelectedObject; Point newPoint = position; newPoint.y -= t; unsigned int randomIndex = 0; if (posInfo.randomlyPickObject) { // TODO: Given that we're picking a random index AND a random location, we don't get the best // distribution here... if (posInfo.doSwitchover) { if (posInfo.switchCount < m_lastObjectSwitchover) { randomIndex = rng.randomInt(posInfo.numSelectedItems - 1); pSrcObject = SelectionManager::instance().getSelection().getSelectedObjectAtIndex(randomIndex); } else { // pick the last object randomIndex = posInfo.numSelectedItems - 1; pSrcObject = SelectionManager::instance().getSelection().getSelectedObjectAtIndex(randomIndex); } } else { // just randomly pick... randomIndex = rng.randomInt(posInfo.numSelectedItems); pSrcObject = SelectionManager::instance().getSelection().getSelectedObjectAtIndex(randomIndex); } // need to update the height of the object BoundaryBox shapeBBox = pSrcObject->getTransformedBoundaryBox(); posInfo.heightOffset = shapeBBox.getExtent().y * 0.5f; } float scaleVariation = 1.0f; if (posInfo.scaleVariation) { // scale variation can be larger or smaller than current object, split around the middle float halfVariation = m_uniformScaleVariation * 0.5f; scaleVariation = 1.0f - halfVariation + (m_uniformScaleVariation * rng.randomFloat()); } float localHeightOffset = posInfo.heightOffset; if (posInfo.scaleVariation) { localHeightOffset *= scaleVariation; } newPoint.y += localHeightOffset; // centre the object's bbox on the surface that was hit // TODO: an option to ratio this based on the surface flatness amount, so that objects on a flat surface // don't have as much as an offset, whereas objects on a steep incline (trees for example) have more of an offset newPoint.y += m_surfaceYOffset; Object* pNewObject = NULL; if (!posInfo.shouldMakeBakedInstances) { pNewObject = pSrcObject->clone(); if (m_alternatingMaterials) { Material* pMat = posInfo.aMaterials[posInfo.altMaterialIndex]; pNewObject->setMaterial(pMat); posInfo.altMaterialIndex++; if (posInfo.altMaterialIndex >= m_numMaterials) posInfo.altMaterialIndex = 0; } } else { if (posInfo.randomlyPickObject) { pNewObject = new CompoundInstance(posInfo.aMultipleSrcCO[randomIndex]); } else { pNewObject = new CompoundInstance(posInfo.pSrcCOForBakedInstances); } } pNewObject->setPosition(Vector(newPoint.x, newPoint.y, newPoint.z)); if (posInfo.scaleVariation) { float currentUScale = pNewObject->transform().getUniformScale(); pNewObject->transform().setUniformScale(currentUScale * scaleVariation); } if (m_alignToSurface) { // work out what the rotation should be to make it roughly flat on the surface - this won't be physically-accurate, but it's // a good starting point Quaternion rotateQ = Quaternion::fromDirections(Normal(0.0f, 1.0f, 0.0f), hitResult.geometryNormal); Matrix4 rotateM = rotateQ.toMatrix(); Vector rotation = rotateM.getRotationYXZ(); if (m_randomYRotation) { rotation.y = rng.randomFloat() * 360.0f; } pNewObject->setRotation(rotation); } else { if (m_randomYRotation) { pNewObject->setRotation(Vector(0.0f, rng.randomFloat() * 360.0f, 0.0f)); } } if (m_drawAsBBox) pNewObject->setDisplayType(eBoundaryBox); sprintf(posInfo.szName, "ScatteredOb_%d", posInfo.createdCount); // calling function needs to increment this pNewObject->setName(posInfo.szName, false); if (m_addToGroup) { posInfo.pNewCO->addObject(pNewObject, false); } else { addObject(scene, pNewObject); } } return true; }