#include "gosFXHeaders.hpp" #include //==========================================================================// // File: gosFX_SpriteCloud.cpp // // Project: gosFX // // Contents: Base gosFX::SpriteCloud Component // //--------------------------------------------------------------------------// // Date Who Modification // // 09/28/98 JTR Created // // // //--------------------------------------------------------------------------// // Copyright (C) 1997-1998, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //==========================================================================// // //############################################################################ //######################## gosFX::SpriteCloud__Specification ############################# //############################################################################ //------------------------------------------------------------------------------ // gosFX::SpriteCloud__Specification::SpriteCloud__Specification( Stuff::MemoryStream *stream, int gfx_version ): ParticleCloud__Specification(gosFX::SpriteCloudClassID, stream, gfx_version) { Check_Pointer(this); Verify(m_class == SpriteCloudClassID); m_totalParticleSize = gosFX::SpriteCloud::ParticleSize; m_particleClassSize = sizeof(gosFX::SpriteCloud::Particle); } //------------------------------------------------------------------------------ // gosFX::SpriteCloud__Specification::SpriteCloud__Specification(): ParticleCloud__Specification(gosFX::SpriteCloudClassID) { Check_Pointer(this); m_totalParticleSize = gosFX::SpriteCloud::ParticleSize; m_particleClassSize = sizeof(gosFX::SpriteCloud::Particle); } //------------------------------------------------------------------------------ // gosFX::SpriteCloud__Specification::~SpriteCloud__Specification() { Check_Object(this); } //------------------------------------------------------------------------------ // gosFX::SpriteCloud__Specification* gosFX::SpriteCloud__Specification::Make( Stuff::MemoryStream *stream, int gfx_version ) { Check_Object(stream); gos_PushCurrentHeap(Heap); SpriteCloud__Specification *spec = new gosFX::SpriteCloud__Specification(stream, gfx_version); gos_PopCurrentHeap(); return spec; } //------------------------------------------------------------------------------ // bool gosFX::SpriteCloud__Specification::IsDataValid(bool fix_data) { Check_Object(this); // //----------------------------------------- // Make sure we don't overwrite MLR buffers //----------------------------------------- // int particle_limit = MidLevelRenderer::Limits::Max_Number_Vertices_Per_Effect/4; if (m_maxParticleCount > particle_limit) { if (fix_data) { m_maxParticleCount = particle_limit; PAUSE(("Warning: Max Particle Count in Effect \"%s\" is too big and has been reset",(char *)m_name)); } else return false; } Stuff::Scalar min,max; m_pScale.ExpensiveComputeRange(&min,&max); if (min<5.0f*Stuff::SMALL) { if (fix_data) { m_pScale.SetMinimum(10.0f*Stuff::SMALL); PAUSE(("Scale of 0 Has been detected in Effect \"%s\" and has been reset",(char *)m_name)); } else return false; } Stuff::Scalar max_offset, min_offset; Stuff::Scalar max_scale, min_scale; m_VSize.ExpensiveComputeRange(&min_scale, &max_scale); Stuff::Scalar lower = min_scale; if (lower > 0.0f) lower = 0.0f; Stuff::Scalar upper = max_scale; // //------------------------------------ // Calculate the worst case UV offsets //------------------------------------ // m_VOffset.ExpensiveComputeRange(&min_offset, &max_offset); lower += min_offset; upper += max_offset; if (upper > 99.0f || lower < -99.0f) { if(fix_data) { m_VOffset.SetCurve(0.0f); m_VOffset.SetCurve(0.0f); m_VSize.SetCurve(1.0f); PAUSE(("Warning: Curve \"VOffset\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name)); } else return false; } m_USize.ExpensiveComputeRange(&min_scale, &max_scale); lower = min_scale; if (lower > 0.0f) lower = 0.0f; upper = max_scale; // //------------------------------------ // Calculate the worst case UV offsets //------------------------------------ // max_offset, min_offset; m_UOffset.ExpensiveComputeRange(&min_offset, &max_offset); lower += min_offset; upper += max_offset; if (upper > 99.0f || lower < -99.0f) { if(fix_data) { m_UOffset.SetCurve(0.0f); m_USize.SetCurve(1.0f); PAUSE(("Warning: Curve \"UOffset\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name)); } else return false; } return BaseClass::IsDataValid(fix_data); } //############################################################################ //############################## gosFX::SpriteCloud ################################ //############################################################################ gosFX::SpriteCloud::ClassData* gosFX::SpriteCloud::DefaultData = NULL; //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( SpriteCloudClassID, "gosFX::SpriteCloud", BaseClass::DefaultData, (Effect::Factory)&Make, (Specification::Factory)&Specification::Make ); Check_Object(DefaultData); } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::TerminateClass() { Check_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //------------------------------------------------------------------------------ // gosFX::SpriteCloud::SpriteCloud( Specification *spec, unsigned flags ): ParticleCloud(DefaultData, spec, flags) { Check_Object(spec); gos_PushCurrentHeap(MidLevelRenderer::EffectHeap); m_cloudImplementation = new MidLevelRenderer::MLRSpriteCloud(spec->m_maxParticleCount); Check_Object(m_cloudImplementation); gos_PopCurrentHeap(); unsigned index = spec->m_maxParticleCount*sizeof(Particle); m_P_localTranslation = Cast_Pointer(Stuff::Point3D*, &m_data[index]); index += spec->m_maxParticleCount*sizeof(Stuff::Point3D); m_P_color = Cast_Pointer(Stuff::RGBAColor*, &m_data[index]); index += spec->m_maxParticleCount*sizeof(Stuff::RGBAColor); m_P_scale = Cast_Pointer(Stuff::Scalar*, &m_data[index]); m_cloudImplementation->SetData( Cast_Pointer(const int *, &m_activeParticleCount), m_P_localTranslation, m_P_color, m_P_scale, m_UVs ); Stuff::Scalar u = spec->m_UOffset.ComputeValue(m_age, m_seed); Stuff::Scalar v = spec->m_VOffset.ComputeValue(m_age, m_seed); Stuff::Scalar u2 = spec->m_USize.ComputeValue(m_age, m_seed); Stuff::Scalar v2 = spec->m_VSize.ComputeValue(m_age, m_seed); u2 += u; v2 += v; m_UVs[0].x = u; m_UVs[0].y = v2; m_UVs[1].x = u2; m_UVs[1].y = v2; m_UVs[2].x = u2; m_UVs[2].y = v; m_UVs[3].x = u; m_UVs[3].y = v; } //------------------------------------------------------------------------------ // gosFX::SpriteCloud::~SpriteCloud() { Check_Object(this); Check_Object(m_cloudImplementation); delete m_cloudImplementation; } //------------------------------------------------------------------------------ // gosFX::SpriteCloud* gosFX::SpriteCloud::Make( Specification *spec, unsigned flags ) { Check_Object(spec); gos_PushCurrentHeap(Heap); SpriteCloud *cloud = new gosFX::SpriteCloud(spec, flags); gos_PopCurrentHeap(); return cloud; } //------------------------------------------------------------------------------ // bool gosFX::SpriteCloud::Execute(ExecuteInfo *info) { Check_Object(this); Check_Object(info); GFX_LOGIC("Execute::SpriteCloud"); // //---------------------------------------- // If we aren't supposed to execute, don't //---------------------------------------- // if (!IsExecuted()) return false; // //------------------------------------------------------------------ // Animate the particles. If it is time for us to die, return false //------------------------------------------------------------------ // if (!BaseClass::Execute(info)) return false; // //----------------------------------------------------------------------- // If there are active particles to animate, get the current center point // of the bounds //----------------------------------------------------------------------- // if (m_activeParticleCount > 0) { GFX_LOGIC("Execute::ComputeBounds::SpriteCloud"); Stuff::ExtentBox box(Stuff::Point3D::Identity, Stuff::Point3D::Identity); Stuff::Point3D *vertex = &m_P_localTranslation[0]; unsigned i=0; // //------------------------------------------------------------------- // If there is no bounds yet, we need to create our extent box around // the first legal point we find //------------------------------------------------------------------- // while (im_age < 1.0f) { Check_Object(vertex); box.maxX = vertex->x; box.minX = vertex->x; box.maxY = vertex->y; box.minY = vertex->y; box.maxZ = vertex->z; box.minZ = vertex->z; ++vertex; ++i; break; } ++vertex; ++i; } // //----------------------------- // Look for the other particles //----------------------------- // while (im_age < 1.0f) { Check_Object(vertex); if (vertex->x > box.maxX) box.maxX = vertex->x; else if (vertex->x < box.minX) box.minX = vertex->x; if (vertex->y > box.maxY) box.maxY = vertex->y; else if (vertex->y < box.minY) box.minY = vertex->y; if (vertex->z > box.maxZ) box.maxZ = vertex->z; else if (vertex->z < box.minZ) box.minZ = vertex->z; } ++vertex; ++i; } // //------------------------------------ // Now, build a info->m_bounds around this box //------------------------------------ // Verify(box.maxX >= box.minX); Verify(box.maxY >= box.minY); Verify(box.maxZ >= box.minZ); Stuff::OBB local_bounds = Stuff::OBB::Identity; local_bounds.axisExtents.x = 0.5f * (box.maxX - box.minX); local_bounds.axisExtents.y = 0.5f * (box.maxY - box.minY); local_bounds.axisExtents.z = 0.5f * (box.maxZ - box.minZ); local_bounds.localToParent(3,0) = box.minX + local_bounds.axisExtents.x; local_bounds.localToParent(3,1) = box.minY + local_bounds.axisExtents.y; local_bounds.localToParent(3,2) = box.minZ + local_bounds.axisExtents.z; local_bounds.sphereRadius = local_bounds.axisExtents.GetLength() + Stuff::SMALL; Stuff::OBB parent_bounds; parent_bounds.Multiply(local_bounds, m_localToParent); info->m_bounds->Union(*info->m_bounds, parent_bounds); } // //---------------------------------------------- // Tell our caller that we get to keep executing //---------------------------------------------- // return true; } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::CreateNewParticle( unsigned index, Stuff::Point3D *translation ) { Check_Object(this); Check_Pointer(translation); // //----------------------------------------------------------------------- // Let our parent do creation, then turn on the particle in the cloud and // set its position //----------------------------------------------------------------------- // BaseClass::CreateNewParticle(index, translation); m_cloudImplementation->TurnOn(index); Verify(m_cloudImplementation->IsOn(index)); m_P_localTranslation[index] = *translation; } //------------------------------------------------------------------------------ // bool gosFX::SpriteCloud::AnimateParticle( unsigned index, const Stuff::LinearMatrix4D *world_to_new_local, Stuff::Time till ) { Check_Object(this); PARTICLE_LOGIC("Sprite"); // //----------------------------------------------------------------------- // If this cloud is unparented, we need to transform the point from local // space into world space and set the internal position/velocity pointers // to these temporary values //----------------------------------------------------------------------- // Particle *particle = GetParticle(index); Check_Object(particle); Stuff::Scalar age = particle->m_age; if (age >= 1.0f) return false; Set_Statistic(Point_Count, Point_Count+1); Stuff::Vector3D *velocity = &particle->m_localLinearVelocity; Stuff::Point3D *translation = &m_P_localTranslation[index]; int sim_mode = GetSimulationMode(); if (sim_mode == DynamicWorldSpaceSimulationMode) { Check_Object(translation); Check_Object(velocity); particle->m_worldLinearVelocity.Multiply(*velocity, m_localToWorld); particle->m_worldTranslation.Multiply(*translation, m_localToWorld); translation = &particle->m_worldTranslation; velocity = &particle->m_worldLinearVelocity; } Check_Object(translation); Check_Object(velocity); // //------------------------------------------------------------------ // First, calculate the drag on the particle. Drag can never assist // velocity //------------------------------------------------------------------ // Stuff::Scalar seed = particle->m_seed; Specification *spec = GetSpecification(); Check_Object(spec); Stuff::Vector3D ether; ether.x = spec->m_pEtherVelocityX.ComputeValue(age, seed); ether.y = spec->m_pEtherVelocityY.ComputeValue(age, seed); ether.z = spec->m_pEtherVelocityZ.ComputeValue(age, seed); Stuff::Vector3D accel(Stuff::Vector3D::Identity); // //------------------------------------------------------------------- // Deal with pseudo-world simulation. In this mode, we interpret the // forces as if they are already in worldspace, and we transform them // back to local space //------------------------------------------------------------------- // Stuff::Scalar drag = -spec->m_pDrag.ComputeValue(age, seed); Max_Clamp(drag, 0.0f); if (sim_mode == StaticWorldSpaceSimulationMode) { Stuff::LinearMatrix4D world_to_effect; world_to_effect.Invert(m_localToWorld); Stuff::Vector3D local_ether; local_ether.MultiplyByInverse(ether, world_to_effect); Stuff::Vector3D rel_vel; rel_vel.Subtract(*velocity, local_ether); accel.Multiply(rel_vel, drag); // //----------------------------------------- // Now, add in acceleration of the particle //----------------------------------------- // Stuff::Vector3D world_accel; world_accel.x = spec->m_pAccelerationX.ComputeValue(age, seed); world_accel.y = spec->m_pAccelerationY.ComputeValue(age, seed); world_accel.z = spec->m_pAccelerationZ.ComputeValue(age, seed); Stuff::Vector3D local_accel; local_accel.Multiply(world_accel, world_to_effect); accel += local_accel; } // //---------------------------------------------------------------------- // Otherwise, just add the forces in the same space the particles are in //---------------------------------------------------------------------- // else { Stuff::Vector3D rel_vel; rel_vel.Subtract(*velocity, ether); accel.Multiply(rel_vel, drag); // //----------------------------------------- // Now, add in acceleration of the particle //----------------------------------------- // accel.x += spec->m_pAccelerationX.ComputeValue(age, seed); accel.y += spec->m_pAccelerationY.ComputeValue(age, seed); accel.z += spec->m_pAccelerationZ.ComputeValue(age, seed); } // //------------------------------------------------- // Compute the particle's new velocity and position //------------------------------------------------- // Stuff::Scalar time_slice = static_cast(till - m_lastRan); velocity->AddScaled(*velocity, accel, time_slice); translation->AddScaled(*translation, *velocity, time_slice); // //--------------------------------------------------------------------- // If we are unparented, we need to transform the velocity and position // data back into the NEW local space //--------------------------------------------------------------------- // if (sim_mode == DynamicWorldSpaceSimulationMode) { Check_Object(world_to_new_local); particle->m_localLinearVelocity.Multiply( particle->m_worldLinearVelocity, *world_to_new_local ); m_P_localTranslation[index].Multiply( particle->m_worldTranslation, *world_to_new_local ); } return true; } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::DestroyParticle(unsigned index) { Check_Object(this); m_cloudImplementation->TurnOff(index); Verify(!m_cloudImplementation->IsOn(index)); BaseClass::DestroyParticle(index); } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::Draw(DrawInfo *info) { Check_Object(this); Check_Object(info); GFX_RENDER("SpriteCloud"); if (m_activeParticleCount) { MidLevelRenderer::DrawEffectInformation dInfo; dInfo.effect = m_cloudImplementation; Specification *spec = GetSpecification(); Check_Object(spec); Check_Pointer(m_P_color); Check_Pointer(m_P_scale); for (unsigned i=0; im_age; if (age < 1.0f) { Stuff::Scalar seed = particle->m_seed; m_P_color[i].red = spec->m_pRed.ComputeValue(age, seed); m_P_color[i].green = spec->m_pGreen.ComputeValue(age, seed); m_P_color[i].blue = spec->m_pBlue.ComputeValue(age, seed); m_P_color[i].alpha = spec->m_pAlpha.ComputeValue(age, seed); m_P_scale[i] = spec->m_pScale.ComputeValue(age, seed); } } dInfo.state.Combine(info->m_state, spec->m_state); dInfo.clippingFlags = info->m_clippingFlags; Stuff::LinearMatrix4D local_to_world; local_to_world.Multiply(m_localToParent, *info->m_parentToWorld); dInfo.effectToWorld = &local_to_world; info->m_clipper->DrawEffect(&dInfo); } BaseClass::Draw(info); } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //------------------------------------------------------------------------------ // void gosFX::SpriteCloud__Specification::BuildDefaults() { Check_Object(this); BaseClass::BuildDefaults(); m_pScale.m_ageCurve.SetCurve(1.0f); m_pScale.m_seeded = false; m_pScale.m_seedCurve.SetCurve(1.0f); m_UOffset.SetCurve(0.0f); m_VOffset.SetCurve(0.0f); m_USize.SetCurve(1.0f); m_VSize.SetCurve(1.0f); }