#include "ShapePerFrameContext.hpp" #include "CustomShape.hpp" #include "MilkdropPresetExceptions.hpp" #include "PerFrameContext.hpp" #include #define REG_VAR(var) \ var = projectm_eval_context_register_variable(perFrameCodeContext, #var); namespace libprojectM { namespace MilkdropPreset { ShapePerFrameContext::ShapePerFrameContext(projectm_eval_mem_buffer gmegabuf, PRJM_EVAL_F (*globalRegisters)[100]) : perFrameCodeContext(projectm_eval_context_create(gmegabuf, globalRegisters)) { } ShapePerFrameContext::~ShapePerFrameContext() { if (perFrameCodeHandle != nullptr) { projectm_eval_code_destroy(perFrameCodeHandle); } if (perFrameCodeContext != nullptr) { projectm_eval_context_destroy(perFrameCodeContext); } } void ShapePerFrameContext::RegisterBuiltinVariables() { projectm_eval_context_reset_variables(perFrameCodeContext); REG_VAR(time); REG_VAR(fps); REG_VAR(frame); REG_VAR(progress); for (int q = 0; q < QVarCount; q++) { std::string qvar = "q" + std::to_string(q + 1); q_vars[q] = projectm_eval_context_register_variable(perFrameCodeContext, qvar.c_str()); } for (int t = 0; t < TVarCount; t++) { std::string tvar = "t" + std::to_string(t + 1); t_vars[t] = projectm_eval_context_register_variable(perFrameCodeContext, tvar.c_str()); } REG_VAR(bass); REG_VAR(mid); REG_VAR(treb); REG_VAR(bass_att); REG_VAR(mid_att); REG_VAR(treb_att); REG_VAR(x); REG_VAR(y); REG_VAR(rad); REG_VAR(ang); REG_VAR(tex_ang); REG_VAR(tex_zoom); REG_VAR(sides); REG_VAR(textured); REG_VAR(instance); REG_VAR(num_inst); REG_VAR(additive); REG_VAR(thick); REG_VAR(r); REG_VAR(g); REG_VAR(b); REG_VAR(a); REG_VAR(r2); REG_VAR(g2); REG_VAR(b2); REG_VAR(a2); REG_VAR(border_r); REG_VAR(border_g); REG_VAR(border_b); REG_VAR(border_a); } void ShapePerFrameContext::LoadStateVariables(const PresetState& state, CustomShape& shape, int inst) { *time = static_cast(state.renderContext.time); *frame = static_cast(state.renderContext.frame); *fps = static_cast(state.renderContext.fps); *progress = static_cast(state.renderContext.progress); *bass = static_cast(state.audioData.bass); *mid = static_cast(state.audioData.mid); *treb = static_cast(state.audioData.treb); *bass_att = static_cast(state.audioData.bassAtt); *mid_att = static_cast(state.audioData.midAtt); *treb_att = static_cast(state.audioData.trebAtt); for (int q = 0; q < QVarCount; q++) { *q_vars[q] = state.frameQVariables[q]; } for (int t = 0; t < TVarCount; t++) { *t_vars[t] = shape.m_tValuesAfterInitCode[t]; } *x = static_cast(shape.m_x); *y = static_cast(shape.m_y); *rad = static_cast(shape.m_radius); *ang = static_cast(shape.m_angle); *tex_zoom = static_cast(shape.m_tex_zoom); *tex_ang = static_cast(shape.m_tex_ang); *sides = static_cast(shape.m_sides); *additive = static_cast(shape.m_additive); *textured = static_cast(shape.m_textured); *num_inst = static_cast(shape.m_instances); *instance = static_cast(inst); *thick = static_cast(shape.m_thickOutline); *r = static_cast(shape.m_r); *g = static_cast(shape.m_g); *b = static_cast(shape.m_b); *a = static_cast(shape.m_a); *r2 = static_cast(shape.m_r2); *g2 = static_cast(shape.m_g2); *b2 = static_cast(shape.m_b2); *a2 = static_cast(shape.m_a2); *border_r = static_cast(shape.m_border_r); *border_g = static_cast(shape.m_border_g); *border_b = static_cast(shape.m_border_b); *border_a = static_cast(shape.m_border_a); } void ShapePerFrameContext::EvaluateInitCode(const std::string& perFrameInitCode, const CustomShape& shape) { if (perFrameInitCode.empty()) { return; } auto* initCode = projectm_eval_code_compile(perFrameCodeContext, perFrameInitCode.c_str()); if (initCode == nullptr) { std::string error; int line; int col; auto* errmsg = projectm_eval_get_error(perFrameCodeContext, &line, &col); if (errmsg) { error = "[ShapePerFrameContext] Could not compile custom shape "; error += std::to_string(shape.m_index); error += " per-frame INIT code: "; error += std::string(errmsg); error += "(L" + std::to_string(line) + " C" + std::to_string(col) + ")"; } else { error = "[ShapePerFrameContext] Could not compile custom shape " + std::to_string(shape.m_index) + " per-frame init code."; } LOG_DEBUG("[ShapePerFrameContext] Failed custom shape per-frame INIT code:\n" + perFrameInitCode); throw MilkdropCompileException(error); } projectm_eval_code_execute(initCode); projectm_eval_code_destroy(initCode); } void ShapePerFrameContext::CompilePerFrameCode(const std::string& perFrameCode, const CustomShape& shape) { if (perFrameCode.empty()) { return; } perFrameCodeHandle = projectm_eval_code_compile(perFrameCodeContext, perFrameCode.c_str()); if (perFrameCodeHandle == nullptr) { std::string error; int line; int col; auto* errmsg = projectm_eval_get_error(perFrameCodeContext, &line, &col); if (errmsg) { error = "[ShapePerFrameContext] Could not compile custom shape "; error += std::to_string(shape.m_index); error += " per-frame code: "; error += errmsg; error += "(L" + std::to_string(line) + " C" + std::to_string(col) + ")"; } else { error = "[ShapePerFrameContext] Could not compile custom shape " + std::to_string(shape.m_index) + " per-frame code."; } LOG_DEBUG("[ShapePerFrameContext] Failed custom shape per-frame code:\n" + perFrameCode); throw MilkdropCompileException(error); } } void ShapePerFrameContext::ExecutePerFrameCode() { if (perFrameCodeHandle != nullptr) { projectm_eval_code_execute(perFrameCodeHandle); } } } // namespace MilkdropPreset } // namespace libprojectM