// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include namespace anki { static Bool mutatorValueExists(const ShaderBinaryMutator& m, MutatorValue val) { for(MutatorValue v : m.m_values) { if(v == val) { return true; } } return false; } Error ParticleEmitterResource2::load(const ResourceFilename& filename, Bool async) { ResourceXmlDocument doc; XmlElement el; ANKI_CHECK(openFileParseXml(filename, doc)); // XmlElement rootEl; ANKI_CHECK(doc.getChildElement("particleEmitter", rootEl)); // XmlElement shaderProgramEl; ANKI_CHECK(rootEl.getChildElement("shaderProgram", shaderProgramEl)); ANKI_CHECK(parseShaderProgram(shaderProgramEl, async)); // XmlElement particleCountEl; ANKI_CHECK(rootEl.getChildElement("particleCount", particleCountEl)); ANKI_CHECK(particleCountEl.getAttributeNumber("value", m_commonProps.m_particleCount)); if(m_commonProps.m_particleCount == 0 || m_commonProps.m_particleCount > kMaxU16) // kMaxU16 is arbitary { ANKI_RESOURCE_LOGE("Can't accept particleCount of: %u", m_commonProps.m_particleCount); return Error::kUserData; } // XmlElement emissionPeriodEl; ANKI_CHECK(rootEl.getChildElement("emissionPeriod", emissionPeriodEl)); ANKI_CHECK(emissionPeriodEl.getAttributeNumber("value", m_commonProps.m_emissionPeriod)); // XmlElement particlesPerEmissionEl; ANKI_CHECK(rootEl.getChildElement("particlesPerEmission", particlesPerEmissionEl)); ANKI_CHECK(particlesPerEmissionEl.getAttributeNumber("value", m_commonProps.m_particlesPerEmission)); if(m_commonProps.m_particlesPerEmission == 0) { ANKI_RESOURCE_LOGE("Can't accept particlesPerEmission of: %u", m_commonProps.m_particlesPerEmission); return Error::kUserData; } // Inputs ANKI_CHECK(rootEl.getChildElementOptional("inputs", el)); if(el) { XmlElement inputEl; ANKI_CHECK(el.getChildElement("input", inputEl)); do { ANKI_CHECK(parseInput(inputEl)); ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl)); } while(inputEl); } return Error::kNone; } Error ParticleEmitterResource2::parseShaderProgram(XmlElement shaderProgramEl, Bool async) { // name CString shaderName; ANKI_CHECK(shaderProgramEl.getAttributeText("name", shaderName)); ResourceString fname; fname.sprintf("ShaderBinaries/%s.ankiprogbin", shaderName.cstr()); ANKI_CHECK(ResourceManager::getSingleton().loadResource(fname, m_prog, async)); // ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog); XmlElement mutatorsEl; ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl)); if(mutatorsEl) { ANKI_CHECK(parseMutators(mutatorsEl, variantInitInfo)); } // Create the GR prog variantInitInfo.addMutation("ANKI_WAVE_SIZE", GrManager::getSingleton().getDeviceCapabilities().m_maxWaveSize); const ShaderProgramResourceVariant* variant; m_prog->getOrCreateVariant(variantInitInfo, variant); if(!variant) { ANKI_RESOURCE_LOGE("Shader variant creation failed"); return Error::kUserData; } m_grProg.reset(&variant->getProgram()); // Create the properties { // Find struct const ShaderBinary& binary = m_prog->getBinary(); const ShaderBinaryStruct* propsStruct = nullptr; for(const ShaderBinaryStruct& strct : binary.m_structs) { if(CString(strct.m_name.getBegin()) == "AnKiParticleEmitterProperties") { propsStruct = &strct; break; } } if(!propsStruct) { ANKI_RESOURCE_LOGE("AnKiParticleEmitterProperties struct not found"); return Error::kUserData; } m_prefilledAnKiParticleEmitterProperties.resize(propsStruct->m_size, 0_U8); m_otherProps.resize(propsStruct->m_members.getSize()); for(U32 i = 0; i < propsStruct->m_members.getSize(); ++i) { const ShaderBinaryStructMember& member = propsStruct->m_members[i]; const U32 memberSize = getShaderVariableDataTypeInfo(member.m_type).m_size; ANKI_ASSERT(member.m_offset + memberSize <= propsStruct->m_size); memcpy(m_prefilledAnKiParticleEmitterProperties.getBegin() + member.m_offset, member.m_defaultValues.getBegin(), memberSize); // Initialize the property with the default values ParticleEmitterResourceProperty& prop = m_otherProps[i]; prop.m_offsetInAnKiParticleEmitterProperties = member.m_offset; prop.m_name = member.m_name.getBegin(); prop.m_dataType = member.m_type; switch(prop.m_dataType) { #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \ case ShaderVariableDataType::k##type: \ { \ ANKI_ASSERT(sizeof(prop.m_##type) == memberSize); \ memcpy(&prop.m_##type, member.m_defaultValues.getBegin(), memberSize); \ break; \ } #include default: ANKI_ASSERT(0); } } } return Error::kNone; } Error ParticleEmitterResource2::parseMutators(XmlElement mutatorsEl, ShaderProgramResourceVariantInitInfo& variantInitInfo) { XmlElement mutatorEl; ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl)); U32 mutatorCount = 0; ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount)); ++mutatorCount; ANKI_ASSERT(mutatorCount > 0); do { // name CString mutatorName; ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName)); if(mutatorName.isEmpty()) { ANKI_RESOURCE_LOGE("Mutator name is empty"); return Error::kUserData; } if(mutatorName.find("ANKI_") == 0) { ANKI_RESOURCE_LOGE("Mutators can't start with ANKI_: %s", mutatorName.cstr()); return Error::kUserData; } // value MutatorValue mval; ANKI_CHECK(mutatorEl.getAttributeNumber("value", mval)); // Find mutator const ShaderBinaryMutator* mutator = m_prog->tryFindMutator(mutatorName); if(!mutator) { ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]); return Error::kUserData; } if(!mutatorValueExists(*mutator, mval)) { ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", mval, mutatorName.cstr()); return Error::kUserData; } variantInitInfo.addMutation(mutatorName, mval); // Advance ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl)); } while(mutatorEl); return Error::kNone; } Error ParticleEmitterResource2::parseInput(XmlElement inputEl) { // Get var name CString varName; ANKI_CHECK(inputEl.getAttributeText("name", varName)); // Find struct const ShaderBinary& binary = m_prog->getBinary(); const ShaderBinaryStruct* propsStruct = nullptr; for(const ShaderBinaryStruct& strct : binary.m_structs) { if(CString(strct.m_name.getBegin()) == "AnKiParticleEmitterProperties") { propsStruct = &strct; break; } } ANKI_ASSERT(propsStruct); // Find the member const ShaderBinaryStructMember* foundMember = nullptr; U32 foundMemberIdx = kMaxU32; for(U32 i = 0; i < propsStruct->m_members.getSize(); ++i) { const ShaderBinaryStructMember& member = propsStruct->m_members[i]; const CString memberName = member.m_name.getBegin(); if(memberName == varName) { foundMember = &member; foundMemberIdx = i; break; } } if(!foundMember) { ANKI_RESOURCE_LOGE("Input not found in AnKiParticleEmitterProperties: %s", varName.cstr()); return Error::kUserData; } const U32 memberSize = getShaderVariableDataTypeInfo(foundMember->m_type).m_size; const U32 memberOffset = foundMember->m_offset; ParticleEmitterResourceProperty& prop = m_otherProps[foundMemberIdx]; // Set the value switch(foundMember->m_type) { #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \ case ShaderVariableDataType::k##type: \ { \ Array arr; \ ANKI_CHECK(inputEl.getAttributeNumbers("value", arr)); \ ANKI_ASSERT(memberOffset + memberSize <= m_prefilledAnKiParticleEmitterProperties.getSize()); \ ANKI_ASSERT(memberSize == sizeof(arr)); \ memcpy(m_prefilledAnKiParticleEmitterProperties.getBegin() + memberOffset, arr.getBegin(), memberSize); \ memcpy(&prop.m_##type, arr.getBegin(), memberSize); \ break; \ } #include default: ANKI_ASSERT(0); break; } return Error::kNone; } } // end namespace anki