feat(web): add audio speaker and particle authoring
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-29 06:30:42 +08:00
parent 81113159dc
commit 43d0c437bb
44 changed files with 2228 additions and 54 deletions

View File

@@ -1585,7 +1585,7 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
type == "moveObjectToCollection" || type == "renameId" || type == "joinObjects" ||
type == "separateMeshFaces" || type == "applyObjectTransform" || type == "setObjectOrigin" ||
type == "setCurveControlPoints" || type == "setCurveHandle" || type == "setCurveTopology" || type == "setCurveSplines" || type == "setSurfaceTopology" || type == "setFontBody" ||
type == "setFontProperties" || type == "setFontAdvanced" || type == "importVFont" || type == "setFontLinks" || type == "setTextSource" || type == "setVolumeProperties" || type == "deleteNonMeshData" ||
type == "setFontProperties" || type == "setFontAdvanced" || type == "importVFont" || type == "setFontLinks" || type == "setTextSource" || type == "setSoundProperties" || type == "setSpeakerProperties" || type == "setParticleSettings" || type == "setVolumeProperties" || type == "deleteNonMeshData" ||
type == "setMetaballElements" || type == "createGreasePencilLayer" ||
type == "removeGreasePencilLayer" || type == "moveGreasePencilLayer" ||
type == "moveGreasePencilFrame" ||
@@ -1999,6 +1999,51 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
engine->authoritative_main, command.value("dataId", "").c_str(),
command.value("source", "").c_str(), main_error);
}
else if (type == "setSoundProperties") {
const json properties = command.value("properties", json::object());
WebSoundPropertiesEdit edit;
edit.volume = properties.value("volume", -1.0f);
edit.attenuation = properties.value("attenuation", -1.0f);
edit.pitch = properties.value("pitch", -1.0f);
edit.min_gain = properties.value("minGain", -1.0f);
edit.max_gain = properties.value("maxGain", -1.0f);
edit.distance = properties.value("distance", -1.0f);
edit.offset_time = properties.value("offsetTime", -1.0);
applied = web_engine_blend_main_set_sound_properties(
engine->authoritative_main, command.value("dataId", "").c_str(), edit, main_error);
}
else if (type == "setSpeakerProperties") {
const json properties = command.value("properties", json::object());
WebSpeakerPropertiesEdit edit;
edit.volume_max = properties.value("volumeMax", -1.0f);
edit.volume_min = properties.value("volumeMin", -1.0f);
edit.distance_max = properties.value("distanceMax", -1.0f);
edit.distance_reference = properties.value("distanceReference", -1.0f);
edit.attenuation = properties.value("attenuation", -1.0f);
edit.cone_angle_outer = properties.value("coneAngleOuter", -1.0f);
edit.cone_angle_inner = properties.value("coneAngleInner", -1.0f);
edit.cone_volume_outer = properties.value("coneVolumeOuter", -1.0f);
edit.volume = properties.value("volume", -1.0f);
edit.pitch = properties.value("pitch", -1.0f);
applied = web_engine_blend_main_set_speaker_properties(
engine->authoritative_main, command.value("dataId", "").c_str(), edit, main_error);
}
else if (type == "setParticleSettings") {
const json properties = command.value("properties", json::object());
WebParticleSettingsEdit edit;
edit.type = properties.value("type", -1);
edit.from = properties.value("from", -1);
edit.distribution = properties.value("distribution", -1);
edit.physics_type = properties.value("physicsType", -1);
edit.total_particles = properties.value("totalParticles", -1);
edit.start = properties.value("start", -1.0f);
edit.end = properties.value("end", -1.0f);
edit.lifetime = properties.value("lifetime", -1.0f);
edit.size = properties.value("size", -1.0f);
edit.draw_size = properties.value("drawSize", -1.0f);
applied = web_engine_blend_main_set_particle_settings(
engine->authoritative_main, command.value("dataId", "").c_str(), edit, main_error);
}
else if (type == "setVolumeProperties") {
WebVolumePropertiesEdit properties;
properties.source_path = command.value("sourcePath", "");

View File

@@ -75,6 +75,8 @@
#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_text_types.h"
#include "DNA_world_types.h"
#include "DNA_userdef_enums.h"
@@ -298,6 +300,30 @@ Volume *find_volume(Main *main, const char *data_id)
return nullptr;
}
bSound *find_sound(Main *main, const char *data_id)
{
if (main == nullptr || data_id == nullptr || strncmp(data_id, "sound:", 6) != 0) return nullptr;
const std::string name(data_id + 6);
for (bSound &sound : main->sounds) if (id_name(sound.id) == name) return &sound;
return nullptr;
}
Speaker *find_speaker(Main *main, const char *data_id)
{
if (main == nullptr || data_id == nullptr || strncmp(data_id, "speaker:", 8) != 0) return nullptr;
const std::string name(data_id + 8);
for (Speaker &speaker : main->speakers) if (id_name(speaker.id) == name) return &speaker;
return nullptr;
}
ParticleSettings *find_particle_settings(Main *main, const char *data_id)
{
if (main == nullptr || data_id == nullptr || strncmp(data_id, "particle-settings:", 18) != 0) return nullptr;
const std::string name(data_id + 18);
for (ParticleSettings &settings : main->particles) if (id_name(settings.id) == name) return &settings;
return nullptr;
}
Light *find_light(Main *main, const char *data_id)
{
if (main == nullptr || data_id == nullptr || strncmp(data_id, "light:", 6) != 0) return nullptr;
@@ -4004,6 +4030,114 @@ bool web_engine_blend_main_set_text_source(WebBlendMainState *state,
return true;
}
bool web_engine_blend_main_set_sound_properties(WebBlendMainState *state,
const char *data_id,
const WebSoundPropertiesEdit &properties,
std::string &error)
{
bSound *sound = find_sound(state != nullptr ? state->main : nullptr, data_id);
if (sound == nullptr || sound->id.lib != nullptr) {
error = "NON_MESH_DATA_UNSUPPORTED: Sound data-block was not found or is linked read-only";
return false;
}
const auto valid = [](const float value, const float maximum) {
return value < 0.0f || !std::isfinite(value) || value > maximum;
};
if ((properties.volume >= 0.0f && valid(properties.volume, 1000000.0f)) ||
(properties.attenuation >= 0.0f && valid(properties.attenuation, 1000000.0f)) ||
(properties.pitch >= 0.0f && valid(properties.pitch, 1000000.0f)) ||
(properties.min_gain >= 0.0f && valid(properties.min_gain, 1000000.0f)) ||
(properties.max_gain >= 0.0f && valid(properties.max_gain, 1000000.0f)) ||
(properties.distance >= 0.0f && valid(properties.distance, 1000000.0f)) ||
(properties.offset_time >= 0.0 && (!std::isfinite(properties.offset_time) || properties.offset_time > 1000000.0))) {
error = "NON_MESH_PROPERTY_INVALID: Sound properties are outside the bounded range";
return false;
}
if (properties.volume >= 0.0f) sound->volume = properties.volume;
if (properties.attenuation >= 0.0f) sound->attenuation = properties.attenuation;
if (properties.pitch >= 0.0f) sound->pitch = properties.pitch;
if (properties.min_gain >= 0.0f) sound->min_gain = properties.min_gain;
if (properties.max_gain >= 0.0f) sound->max_gain = properties.max_gain;
if (properties.distance >= 0.0f) sound->distance = properties.distance;
if (properties.offset_time >= 0.0) sound->offset_time = properties.offset_time;
return true;
}
bool web_engine_blend_main_set_speaker_properties(WebBlendMainState *state,
const char *data_id,
const WebSpeakerPropertiesEdit &properties,
std::string &error)
{
Speaker *speaker = find_speaker(state != nullptr ? state->main : nullptr, data_id);
if (speaker == nullptr || speaker->id.lib != nullptr) {
error = "NON_MESH_DATA_UNSUPPORTED: Speaker data-block was not found or is linked read-only";
return false;
}
const auto valid = [](const float value, const float maximum) {
return value < 0.0f || !std::isfinite(value) || value > maximum;
};
if ((properties.volume_max >= 0.0f && valid(properties.volume_max, 1000000.0f)) ||
(properties.volume_min >= 0.0f && valid(properties.volume_min, 1000000.0f)) ||
(properties.distance_max >= 0.0f && valid(properties.distance_max, 1000000000.0f)) ||
(properties.distance_reference >= 0.0f && valid(properties.distance_reference, 1000000000.0f)) ||
(properties.attenuation >= 0.0f && valid(properties.attenuation, 1000000000.0f)) ||
(properties.cone_angle_outer >= 0.0f && valid(properties.cone_angle_outer, 360.0f)) ||
(properties.cone_angle_inner >= 0.0f && valid(properties.cone_angle_inner, 360.0f)) ||
(properties.cone_volume_outer >= 0.0f && valid(properties.cone_volume_outer, 1.0f)) ||
(properties.volume >= 0.0f && valid(properties.volume, 1000000.0f)) ||
(properties.pitch >= 0.0f && valid(properties.pitch, 1000000.0f))) {
error = "NON_MESH_PROPERTY_INVALID: Speaker properties are outside the bounded range";
return false;
}
if (properties.volume_max >= 0.0f) speaker->volume_max = properties.volume_max;
if (properties.volume_min >= 0.0f) speaker->volume_min = properties.volume_min;
if (properties.distance_max >= 0.0f) speaker->distance_max = properties.distance_max;
if (properties.distance_reference >= 0.0f) speaker->distance_reference = properties.distance_reference;
if (properties.attenuation >= 0.0f) speaker->attenuation = properties.attenuation;
if (properties.cone_angle_outer >= 0.0f) speaker->cone_angle_outer = properties.cone_angle_outer;
if (properties.cone_angle_inner >= 0.0f) speaker->cone_angle_inner = properties.cone_angle_inner;
if (properties.cone_volume_outer >= 0.0f) speaker->cone_volume_outer = properties.cone_volume_outer;
if (properties.volume >= 0.0f) speaker->volume = properties.volume;
if (properties.pitch >= 0.0f) speaker->pitch = properties.pitch;
return true;
}
bool web_engine_blend_main_set_particle_settings(WebBlendMainState *state,
const char *data_id,
const WebParticleSettingsEdit &properties,
std::string &error)
{
ParticleSettings *settings = find_particle_settings(state != nullptr ? state->main : nullptr, data_id);
if (settings == nullptr || settings->id.lib != nullptr) {
error = "NON_MESH_DATA_UNSUPPORTED: ParticleSettings data-block was not found or is linked read-only";
return false;
}
if ((properties.type >= 0 && properties.type > 2) || (properties.from >= 0 && properties.from > 4) ||
(properties.distribution >= 0 && properties.distribution > 2) || (properties.physics_type >= 0 && properties.physics_type > 4) ||
(properties.total_particles >= 0 && properties.total_particles > 1000000)) {
error = "NON_MESH_PROPERTY_INVALID: Particle enum or count is outside the bounded range";
return false;
}
const auto valid = [](const float value) { return value < 0.0f || !std::isfinite(value) || value > 1000000.0f; };
if ((properties.start >= 0.0f && valid(properties.start)) || (properties.end >= 0.0f && valid(properties.end)) ||
(properties.lifetime >= 0.0f && valid(properties.lifetime)) || (properties.size >= 0.0f && valid(properties.size)) ||
(properties.draw_size >= 0.0f && valid(properties.draw_size))) {
error = "NON_MESH_PROPERTY_INVALID: Particle numeric settings are outside the bounded range";
return false;
}
if (properties.type >= 0) settings->type = eParticleType(properties.type);
if (properties.from >= 0) settings->from = eParticleFrom(properties.from);
if (properties.distribution >= 0) settings->distr = eParticleDistribution(properties.distribution);
if (properties.physics_type >= 0) settings->phystype = eParticlePhysicsType(properties.physics_type);
if (properties.total_particles >= 0) settings->totpart = properties.total_particles;
if (properties.start >= 0.0f) settings->sta = properties.start;
if (properties.end >= 0.0f) settings->end = properties.end;
if (properties.lifetime >= 0.0f) settings->lifetime = properties.lifetime;
if (properties.size >= 0.0f) settings->size = properties.size;
if (properties.draw_size >= 0.0f) settings->draw_size = properties.draw_size;
return true;
}
bool web_engine_blend_main_set_volume_properties(WebBlendMainState *state,
const char *data_id,
const WebVolumePropertiesEdit &properties,

View File

@@ -76,6 +76,42 @@ struct WebVolumePropertiesEdit {
float velocity_scale = 1.0f;
};
struct WebSoundPropertiesEdit {
float volume = -1.0f;
float attenuation = -1.0f;
float pitch = -1.0f;
float min_gain = -1.0f;
float max_gain = -1.0f;
float distance = -1.0f;
double offset_time = -1.0;
};
struct WebSpeakerPropertiesEdit {
float volume_max = -1.0f;
float volume_min = -1.0f;
float distance_max = -1.0f;
float distance_reference = -1.0f;
float attenuation = -1.0f;
float cone_angle_outer = -1.0f;
float cone_angle_inner = -1.0f;
float cone_volume_outer = -1.0f;
float volume = -1.0f;
float pitch = -1.0f;
};
struct WebParticleSettingsEdit {
int type = -1;
int from = -1;
int distribution = -1;
int physics_type = -1;
int total_particles = -1;
float start = -1.0f;
float end = -1.0f;
float lifetime = -1.0f;
float size = -1.0f;
float draw_size = -1.0f;
};
struct WebGreasePencilPointEdit {
std::array<float, 3> position = {0.0f, 0.0f, 0.0f};
float radius = 0.01f;
@@ -370,6 +406,18 @@ bool web_engine_blend_main_set_text_source(WebBlendMainState *state,
const char *data_id,
const char *source,
std::string &error);
bool web_engine_blend_main_set_sound_properties(WebBlendMainState *state,
const char *data_id,
const WebSoundPropertiesEdit &properties,
std::string &error);
bool web_engine_blend_main_set_speaker_properties(WebBlendMainState *state,
const char *data_id,
const WebSpeakerPropertiesEdit &properties,
std::string &error);
bool web_engine_blend_main_set_particle_settings(WebBlendMainState *state,
const char *data_id,
const WebParticleSettingsEdit &properties,
std::string &error);
bool web_engine_blend_main_set_volume_properties(WebBlendMainState *state,
const char *data_id,
const WebVolumePropertiesEdit &properties,