double InteractionRays::score(bool visibilityOptional, const ml::TriMeshRayAcceleratorf &occluder, const mat4f &modelToWorld) const { double score = 0.0; double maxScore = (double)rays.size(); for (const auto &r : rays) { if (visibilityOptional) { score += r.score; } else if (r.visible) { // // check to see if the ray is occluded by occluder // bool visible = true; Rayf modelRay = modelToWorld.getInverse() * r.ray; auto intersection = occluder.intersect(modelRay); if (intersection.valid()) { double centroidDist = ml::dist(r.ray.origin(), r.centroidPos); double intersectionDist = ml::dist(r.ray.origin(), modelToWorld.transformAffine(intersection.getSurfacePosition())); if (intersectionDist < centroidDist) visible = false; } if (visible) score += r.score; } } if (maxScore == 0.0) return 0.0; return score / maxScore; }
void Synthesizer::makeInteractionMapRays(const DisembodiedObject &object, const mat4f &modelToWorld, const Agent &agent, const string &interactionMapName, InteractionRays &result) const { const string &categoryName = object.model->categoryName; result.rays.clear(); if (!database->interactionMaps.hasInteractionMapEntry(object.modelId, interactionMapName)) { return; } const InteractionMapEntry &entry = database->interactionMaps.getInteractionMapEntry(object.modelId, interactionMapName); const auto ¢roids = entry.centroids; if (centroids.size() == 0 || (centroids.size() > 10 && synthParams().scanName == "dining-large-nowalld")) { return; } vector < pair<const ml::TriMeshAcceleratorBVHf*, mat4f> > allAccelerators; for (UINT objectIndex = 0; objectIndex < scene.objects.size(); objectIndex++) { // // skip the base architecture, since collisions with that are very unlikely to occlude things // if (object.contactType == ContactWall || objectIndex != 0) { const auto &object = scene.objects[objectIndex]; const auto &accelerator = assets->loadModel(*graphics, object.object.modelId).getAcceleratorOriginal(); allAccelerators.push_back(std::make_pair(&accelerator, object.modelToWorld.getInverse())); } } const auto &selfAccelerator = assets->loadModel(*graphics, object.modelId).getAcceleratorOriginal(); allAccelerators.push_back(std::make_pair(&selfAccelerator, modelToWorld.getInverse())); result.rays.resize(centroids.size()); bool visibilityOptional = (interactionMapName == "backSupport" || interactionMapName == "hips"); vec3f rayOrigin = agent.headPos; if (interactionMapName == "fingertip") { rayOrigin = agent.handPos(); } if (interactionMapName == "backSupport") { rayOrigin = agent.spinePos(); } UINT centroidIndex = 0; for (const auto ¢roid : centroids) { InteractionRayEntry &rayEntry = result.rays[centroidIndex]; rayEntry.centroidPos = modelToWorld * centroid.pos; rayEntry.ray = ml::Rayf(rayOrigin, rayEntry.centroidPos - rayOrigin); rayEntry.score = 1.0; rayEntry.visible = false; ml::TriMeshRayAcceleratorf::Intersection intersection; UINT intersectObjectIndex; if (ml::TriMeshRayAcceleratorf::getFirstIntersectionTransform(rayEntry.ray, allAccelerators, intersection, intersectObjectIndex)) { // // NOTE: intersection is in model space. // double centroidDist = ml::dist(rayOrigin, rayEntry.centroidPos); double intersectDist = ml::dist(rayOrigin, modelToWorld.transformAffine(intersection.getSurfacePosition())); rayEntry.visible = (fabs(centroidDist - intersectDist) < 0.01); if (visibilityOptional || rayEntry.visible) { vec3f surfaceNormal = modelToWorld.transformNormalAffine(intersection.getSurfaceNormal()).getNormalized(); double minNormalAngle = std::min(vec3f::angleBetween( surfaceNormal, rayEntry.ray.direction()), vec3f::angleBetween(-surfaceNormal, rayEntry.ray.direction())); double cosineVisiblityScore = std::max(0.0, cos(ml::math::degreesToRadians(minNormalAngle))); double score = 1.0; if (interactionMapName == "gaze") { // for monitors, gaze is better when directly viewing the object head-on //if (categoryName == "Monitor" || categoryName == "Laptop") score = cosineVisiblityScore; } if (interactionMapName == "fingertip") { score -= 0.01f * centroidDist; } if (interactionMapName == "backSupport") { // back support only counts as visible when it is behind the agent if ((rayEntry.ray.direction() | agent.gazeDir) > 0.0f) { score = 0.0; } else { // back support is best when directly behind the agent score = -(rayEntry.ray.direction() | agent.gazeDir); } } rayEntry.score = score; } } centroidIndex++; } }