void ParticleSystem::spawnFace (const FaceScatter& faceScatter, const ParseOBJ& parseObj, const Color4& color, const Matrix4& transform, int materialIndex) { const Color4unorm8 c(color.pow(1.0f / SmokeVolumeSet::PARTICLE_GAMMA)); Random rnd(10000, false); m_particle.resize(0); m_physicsData.resize(0); AABox bounds; for (ParseOBJ::GroupTable::Iterator git = parseObj.groupTable.begin(); git.isValid(); ++git) { const shared_ptr<ParseOBJ::Group>& group = git->value; for (ParseOBJ::MeshTable::Iterator mit = group->meshTable.begin(); mit.isValid(); ++mit) { const shared_ptr<ParseOBJ::Mesh>& mesh = mit->value; for (int f = 0; f < mesh->faceArray.size(); ++f) { if (rnd.uniform() <= faceScatter.particlesPerFace) { const ParseOBJ::Face& face = mesh->faceArray[f]; Particle& particle = m_particle.next(); PhysicsData& physicsData = m_physicsData.next(); // Use the average vertex as the position Point3 vrt[10]; particle.position = Point3::zero(); for (int v = 0; v < face.size(); ++v) { vrt[v] = (transform * Vector4(parseObj.vertexArray[face[v].vertex], 1.0f)).xyz(); particle.position += vrt[v]; } particle.position /= float(face.size()); const Vector3& normal = (vrt[1] - vrt[0]).cross(vrt[2] - vrt[0]).direction(); particle.position += faceScatter.explodeDistance * normal; particle.normal = normal; particle.materialIndex = materialIndex; particle.color = c; particle.angle = rnd.uniform(0.0f, 2.0f) * pif(); particle.radius = min(faceScatter.maxRadius, faceScatter.radiusScaleFactor * pow((particle.position - vrt[0]).length(), faceScatter.radiusExponent)); particle.emissive = 0; physicsData.angularVelocity = rnd.uniform(-1.0f, 1.0f) * (360.0f * units::degrees()) / (20.0f * units::seconds()); bounds.merge(particle.position); } } // face } // mesh } // group // Center if (faceScatter.moveCenterToOrigin) { const Vector3& offset = -bounds.center(); for (int i = 0; i < m_particle.size(); ++i) { m_particle[i].position += offset; } m_lastObjectSpaceAABoxBounds = bounds + offset; } else { m_lastObjectSpaceAABoxBounds = bounds; } }
void ArticulatedViewer::onInit(const String& filename) { ArticulatedModel::clearCache(); Texture::clearCache(); m_filename = filename; m_selectedPart = NULL; m_selectedMesh = NULL; m_selectedTriangleIndex = -1; m_numFaces = 0; m_numVertices = 0; m_shadowMapDirty = true; UniversalMaterial::clearCache(); const RealTime start = System::time(); if (toLower(FilePath::ext(filename)) == "any") { if ((toLower(FilePath::ext(FilePath::base(filename))) == "material") || (toLower(FilePath::ext(FilePath::base(filename))) == "universalmaterial")) { // Assume that this is an .UniversalMaterial.Any file. Load a square and apply the material Any any(Any::TABLE, "ArticulatedModel::Specification"); any["filename"] = "model/crate/crate4xtex.obj"; any["stripMaterials"] = true; any["preprocess"] = Any(Any::ARRAY); Any arg(Any::ARRAY, "setMaterial"); arg.append(Any(Any::ARRAY, "all")); arg.append(Any::fromFile(filename)); any["preprocess"].append(arg); Any arg2(Any::ARRAY, "setTwoSided"); arg2.append(Any(Any::ARRAY, "all")); arg2.append(true); any["preprocess"].append(arg2); m_model = ArticulatedModel::create(ArticulatedModel::Specification(any)); } else { // Assume that this is an .ArticulatedModel.Any file Any any; any.load(filename); m_model = ArticulatedModel::create(ArticulatedModel::Specification(any)); } } else { m_model = ArticulatedModel::fromFile(filename); } debugPrintf("%s loaded in %f seconds\n", filename.c_str(), System::time() - start); Array<shared_ptr<Surface> > arrayModel; if (m_model->usesSkeletalAnimation()) { Array<G3D::String> animationNames; m_model->getAnimationNames(animationNames); // TODO: Add support for selecting animations. m_model->getAnimation(animationNames[0], m_animation); m_animation.getCurrentPose(0.0f, m_pose); } m_model->pose(arrayModel, CFrame(), m_pose); m_model->countTrianglesAndVertices(m_numFaces, m_numVertices); m_scale = 1; m_offset = Vector3::zero(); bool overwrite = true; // Find the size of the bounding box of the entire model AABox bounds; if (arrayModel.size() > 0) { for (int x = 0; x < arrayModel.size(); ++x) { //merges the bounding boxes of all the seperate parts into the bounding box of the entire object AABox temp; CFrame cframe; arrayModel[x]->getCoordinateFrame(cframe); arrayModel[x]->getObjectSpaceBoundingBox(temp); Box partBounds = cframe.toWorldSpace(temp); // Some models have screwed up bounding boxes if (partBounds.extent().isFinite()) { if (overwrite) { partBounds.getBounds(bounds); overwrite = false; } else { partBounds.getBounds(temp); bounds.merge(temp); } } } if (overwrite) { // We never found a part with a finite bounding box bounds = AABox(Vector3::zero()); } Vector3 extent = bounds.extent(); Vector3 center = bounds.center(); // Scale to X units float scale = 1.0f / max(extent.x, max(extent.y, extent.z)); if (scale <= 0) { scale = 1; } if (! isFinite(scale)) { scale = 1; } m_scale = scale; scale *= VIEW_SIZE; m_offset = -scale * center; if (! center.isFinite()) { center = Vector3(); } // Transform parts in-place m_model->scaleWholeModel(scale); ArticulatedModel::CleanGeometrySettings csg; // Merging vertices is slow and topology hasn't changed at all, so preclude vertex merging csg.allowVertexMerging = false; m_model->cleanGeometry(csg); } // Get the newly transformed animation if (m_model->usesSkeletalAnimation()) { Array<String> animationNames; m_model->getAnimationNames(animationNames); // TODO: Add support for selecting animations. m_model->getAnimation(animationNames[0], m_animation); m_animation.getCurrentPose(0.0f, m_pose); } // saveGeometry(); }
void ShadowMap::computeMatrices (const shared_ptr<Light>& light, AABox sceneBounds, CFrame& lightFrame, Projection& lightProjection, Matrix4& lightProjectionMatrix, float lightProjX, float lightProjY, float lightProjNearMin, float lightProjFarMax, float intensityCutoff) { if (! sceneBounds.isFinite() || sceneBounds.isEmpty()) { // Produce some reasonable bounds sceneBounds = AABox(Point3(-20, -20, -20), Point3(20, 20, 20)); } lightFrame = light->frame(); if (light->type() == Light::Type::DIRECTIONAL) { Point3 center = sceneBounds.center(); if (! center.isFinite()) { center = Point3::zero(); } // Move directional light away from the scene. It must be far enough to see all objects lightFrame.translation = -lightFrame.lookVector() * min(1e6f, max(sceneBounds.extent().length() / 2.0f, lightProjNearMin, 30.0f)) + center; } const CFrame& f = lightFrame; float lightProjNear = finf(); float lightProjFar = 0.0f; // TODO: for a spot light, only consider objects this light can see // Find nearest and farthest corners of the scene bounding box for (int c = 0; c < 8; ++c) { const Vector3& v = sceneBounds.corner(c); const float distance = -f.pointToObjectSpace(v).z; lightProjNear = min(lightProjNear, distance); lightProjFar = max(lightProjFar, distance); } // Don't let the near get too close to the source, and obey // the specified hint. lightProjNear = max(lightProjNearMin, lightProjNear); // Don't bother tracking shadows past the effective radius lightProjFar = min(light->effectSphere(intensityCutoff).radius, lightProjFar); lightProjFar = max(lightProjNear + 0.1f, min(lightProjFarMax, lightProjFar)); debugAssert(lightProjNear < lightProjFar); if (light->type() != Light::Type::DIRECTIONAL) { // TODO: Square spot // Spot light; we can set the lightProj bounds intelligently alwaysAssertM(light->spotHalfAngle() <= halfPi(), "Spot light with shadow map and greater than 180-degree bounds"); // The cutoff is half the angle of extent (See the Red Book, page 193) const float angle = light->spotHalfAngle(); lightProjX = tan(angle) * lightProjNear; // Symmetric in x and y lightProjY = lightProjX; lightProjectionMatrix = Matrix4::perspectiveProjection (-lightProjX, lightProjX, -lightProjY, lightProjY, lightProjNear, lightProjFar); } else if (light->type() == Light::Type::DIRECTIONAL) { // Directional light // Construct a projection and view matrix for the camera so we can // render the scene from the light's point of view // // Since we're working with a directional light, // we want to make the center of projection for the shadow map // be in the direction of the light but at a finite distance // to preserve z precision. lightProjectionMatrix = Matrix4::orthogonalProjection (-lightProjX, lightProjX, -lightProjY, lightProjY, lightProjNear, lightProjFar); } else { // Omni light. Nothing good can happen here, but at least we // generate something lightProjectionMatrix = Matrix4::perspectiveProjection (-lightProjX, lightProjX, -lightProjY, lightProjY, lightProjNear, lightProjFar); } float fov = atan2(lightProjX, lightProjNear) * 2.0f; lightProjection.setFieldOfView(fov, FOVDirection::HORIZONTAL); lightProjection.setNearPlaneZ(-lightProjNear); lightProjection.setFarPlaneZ(-lightProjFar); }
RayGridIterator::RayGridIterator (Ray ray, const Vector3int32& numCells, const Vector3& cellSize, const Point3& gridOrigin, const Point3int32& gridOriginIndex) : m_numCells(numCells), m_enterDistance(0.0f), m_ray(ray), m_cellSize(cellSize), m_insideGrid(true) { if (gridOrigin.nonZero()) { // Change to the grid's reference frame ray = Ray::fromOriginAndDirection(ray.origin() - gridOrigin, ray.direction()); } ////////////////////////////////////////////////////////////////////// // See if the ray begins inside the box const AABox gridBounds(Vector3::zero(), Vector3(numCells) * cellSize); bool startsOutside = false; bool inside = false; Point3 startLocation = ray.origin(); const bool passesThroughGrid = CollisionDetection::rayAABox (ray, Vector3(1,1,1) / ray.direction(), gridBounds, gridBounds.center(), square(gridBounds.extent().length() * 0.5f), startLocation, inside); if (! inside) { if (passesThroughGrid) { // Back up slightly so that we immediately hit the // start location. The precision here is tricky--if // the ray strikes at a very glancing angle, we need // to move a large distance along the ray to enter the // grid. If the ray strikes head on, we only need to // move a small distance. m_enterDistance = (ray.origin() - startLocation).length() - 0.0001f; startLocation = ray.origin() + ray.direction() * m_enterDistance; startsOutside = true; } else { // The ray never hits the grid m_insideGrid = false; } } ////////////////////////////////////////////////////////////////////// // Find the per-iteration variables for (int a = 0; a < 3; ++a) { m_index[a] = floor(startLocation[a] / cellSize[a]); m_tDelta[a] = cellSize[a] / abs(ray.direction()[a]); m_step[a] = sign(ray.direction()[a]); // Distance to the edge fo the cell along the ray direction float d = startLocation[a] - m_index[a] * cellSize[a]; if (m_step[a] > 0) { // Measure from the other edge d = cellSize[a] - d; // Exit on the high side m_boundaryIndex[a] = m_numCells[a]; } else { // Exit on the low side (or never) m_boundaryIndex[a] = -1; } debugAssert(d >= 0 && d <= cellSize[a]); if (ray.direction()[a] != 0) { m_exitDistance[a] = d / abs(ray.direction()[a]) + m_enterDistance; } else { // Ray is parallel to this partition axis. // Avoid dividing by zero, which could be NaN if d == 0 m_exitDistance[a] = inf(); } } if (gridOriginIndex.nonZero()) { // Offset the grid coordinates m_boundaryIndex += gridOriginIndex; m_index += gridOriginIndex; } if (startsOutside) { // Let the increment operator bring us into the first cell // so that the starting axis is initialized correctly. ++(*this); } }