From dcacf189e67a739d5a11e3bfdac4397926193bf6 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Wed, 28 Jun 2017 23:21:42 +0200 Subject: [PATCH] Initial code * Shape Filter * Displacement Filter + Example Data --- CMakeLists.txt | 130 ++++++++++++ source/filter-displacement.cpp | 261 ++++++++++++++++++++++++ source/filter-displacement.h | 88 ++++++++ source/filter-shape.cpp | 357 +++++++++++++++++++++++++++++++++ source/filter-shape.h | 84 ++++++++ source/gs-helper.cpp | 111 ++++++++++ source/gs-helper.h | 58 ++++++ source/plugin.cpp | 61 ++++++ source/plugin.h | 59 ++++++ source/strings.h | 25 +++ source/version.h.in | 25 +++ 11 files changed, 1259 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 source/filter-displacement.cpp create mode 100644 source/filter-displacement.h create mode 100644 source/filter-shape.cpp create mode 100644 source/filter-shape.h create mode 100644 source/gs-helper.cpp create mode 100644 source/gs-helper.h create mode 100644 source/plugin.cpp create mode 100644 source/plugin.h create mode 100644 source/strings.h create mode 100644 source/version.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1100f42 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,130 @@ +cmake_minimum_required(VERSION 2.8.12) +PROJECT(obs-stream-effects) + +################################################################################ +# Version +################################################################################ +SET(VERSION_MAJOR 0) +SET(VERSION_MINOR 1) +SET(VERSION_PATCH 0) +#configure_file( +# "${PROJECT_SOURCE_DIR}/#Resources/package.in.bat" +# "${PROJECT_SOURCE_DIR}/#Resources/package.bat" +#) +#configure_file( +# "${PROJECT_SOURCE_DIR}/#Resources/Installer.in.iss" +# "${PROJECT_SOURCE_DIR}/#Resources/Installer.iss" +#) +configure_file( + "${PROJECT_SOURCE_DIR}/source/version.h.in" + "${PROJECT_BINARY_DIR}/source/version.h" +) + +################################################################################ +# Code +################################################################################ +SET(obs-stream-effects_HEADERS + "${PROJECT_SOURCE_DIR}/source/plugin.h" + "${PROJECT_SOURCE_DIR}/source/filter-displacement.h" + "${PROJECT_SOURCE_DIR}/source/filter-shape.h" + "${PROJECT_SOURCE_DIR}/source/gs-helper.h" + "${PROJECT_BINARY_DIR}/source/version.h" + "${PROJECT_SOURCE_DIR}/source/strings.h" +) +SET(obs-stream-effects_SOURCES + "${PROJECT_SOURCE_DIR}/source/plugin.cpp" + "${PROJECT_SOURCE_DIR}/source/filter-displacement.cpp" + "${PROJECT_SOURCE_DIR}/source/filter-shape.cpp" + "${PROJECT_SOURCE_DIR}/source/gs-helper.cpp" +) +SET(obs-stream-effects_LIBRARIES +) + +################################################################################ +# Standalone and OBS Studio Build Data +################################################################################ +if(DepsPath) + # OBS Studio Specific + + # Directories + INCLUDE_DIRECTORIES( + "${CMAKE_SOURCE_DIR}" + "${PROJECT_BINARY_DIR}" + "${PROJECT_BINARY_DIR}/source" + "${PROJECT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/source" + ) + SET(LIBOBS_LIBRARIES libobs) +else() + # Standlone Specific + + SET(PATH_OBSStudio "" CACHE PATH "OBS Studio Source Code Directory") + if(PATH_OBSStudio STREQUAL "") + message(FATAL_ERROR "PATH_OBSStudio not set!") + return() + endif() + if(NOT EXISTS "${PATH_OBSStudio}/libobs/obs-module.h") + message(FATAL_ERROR "PATH_OBSStudio invalid!") + return() + endif() + + # Find OBS Libraries + SET(obsPath "${PATH_OBSStudio}") + INCLUDE("${PATH_OBSStudio}/cmake/external/Findlibobs.cmake") + + # Compiling + INCLUDE_DIRECTORIES( + "${PROJECT_BINARY_DIR}" + "${PROJECT_BINARY_DIR}/source" + "${PROJECT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/source" + "${PATH_OBSStudio}" + ) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +################################################################################ +# Build +################################################################################ +ADD_LIBRARY(obs-stream-effects MODULE + ${obs-stream-effects_HEADERS} + ${obs-stream-effects_SOURCES} +) +TARGET_LINK_LIBRARIES(obs-stream-effects + ${LIBOBS_LIBRARIES} + ${obs-stream-effects_LIBRARIES} +) + +# All Warnings, Extra Warnings, Pedantic +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") +endif() + +if(DepsPath) + install_obs_plugin_with_data(obs-stream-effects data) +else() + math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") + add_custom_command(TARGET obs-stream-effects POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${PROJECT_SOURCE_DIR}/data" + "${PROJECT_SOURCE_DIR}/#Build/data/obs-plugins/obs-stream-effects" + ) + add_custom_command(TARGET obs-stream-effects POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "$" + "${PROJECT_SOURCE_DIR}/#Build/obs-plugins/${BITS}bit/$" + ) + add_custom_command(TARGET obs-stream-effects POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "$/obs-stream-effects.pdb" + "${PROJECT_SOURCE_DIR}/#Build/obs-plugins/${BITS}bit/obs-stream-effects.pdb" + ) +endif() diff --git a/source/filter-displacement.cpp b/source/filter-displacement.cpp new file mode 100644 index 0000000..b009b7d --- /dev/null +++ b/source/filter-displacement.cpp @@ -0,0 +1,261 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "filter-displacement.h" + +Filter::Displacement::Displacement() { + memset(&sourceInfo, 0, sizeof(obs_source_info)); + sourceInfo.id = "obs-stream-effects-displacement"; + sourceInfo.type = OBS_SOURCE_TYPE_FILTER; + sourceInfo.output_flags = OBS_SOURCE_VIDEO; + sourceInfo.get_name = get_name; + sourceInfo.get_defaults = get_defaults; + sourceInfo.get_properties = get_properties; + + sourceInfo.create = create; + sourceInfo.destroy = destroy; + sourceInfo.update = update; + sourceInfo.activate = activate; + sourceInfo.deactivate = deactivate; + sourceInfo.show = show; + sourceInfo.hide = hide; + sourceInfo.video_tick = video_tick; + sourceInfo.video_render = video_render; + + obs_register_source(&sourceInfo); +} + +Filter::Displacement::~Displacement() { + +} + +const char * Filter::Displacement::get_name(void *) { + return "Displacement Filter"; +} + +void * Filter::Displacement::create(obs_data_t *data, obs_source_t *source) { + return new Instance(data, source); +} + +void Filter::Displacement::destroy(void *ptr) { + delete reinterpret_cast(ptr); +} + +uint32_t Filter::Displacement::get_width(void *ptr) { + return reinterpret_cast(ptr)->get_width(); +} + +uint32_t Filter::Displacement::get_height(void *ptr) { + return reinterpret_cast(ptr)->get_height(); +} + +void Filter::Displacement::get_defaults(obs_data_t *data) { + char* disp = obs_module_file("displacement.png"); + obs_data_set_default_string(data, "file", disp); + obs_data_set_default_double(data, "ratio", 0); + obs_data_set_default_double(data, "scale", 0); + bfree(disp); +} + +obs_properties_t * Filter::Displacement::get_properties(void *ptr) { + obs_properties_t *pr = obs_properties_create(); + + std::string path = ""; + if (ptr) + path = reinterpret_cast(ptr)->get_file(); + + obs_properties_add_path(pr, "file", "File", obs_path_type::OBS_PATH_FILE, + "Images (*.png *.jpeg *.jpg *.bmp);*.png *.jpeg *.jpg *.bmp;;All Files (*)", path.c_str()); + obs_properties_add_float_slider(pr, "ratio", "Distance (%)", 0, 1, 0.01); + obs_properties_add_float_slider(pr, "scale", "Strength", -1000, 1000, 0.01); + return pr; +} + +void Filter::Displacement::update(void *ptr, obs_data_t *data) { + reinterpret_cast(ptr)->update(data); +} + +void Filter::Displacement::activate(void *ptr) { + reinterpret_cast(ptr)->activate(); +} + +void Filter::Displacement::deactivate(void *ptr) { + reinterpret_cast(ptr)->deactivate(); +} + +void Filter::Displacement::show(void *ptr) { + reinterpret_cast(ptr)->show(); +} + +void Filter::Displacement::hide(void *ptr) { + reinterpret_cast(ptr)->hide(); +} + +void Filter::Displacement::video_tick(void *ptr, float time) { + reinterpret_cast(ptr)->video_tick(time); +} + +void Filter::Displacement::video_render(void *ptr, gs_effect_t *effect) { + reinterpret_cast(ptr)->video_render(effect); +} + +Filter::Displacement::Instance::Instance(obs_data_t *data, obs_source_t *context) { + this->dispmap.texture = nullptr; + this->dispmap.createTime = 0; + this->dispmap.modifiedTime = 0; + this->dispmap.size = 0; + this->timer = 0; + this->context = context; + + obs_enter_graphics(); + char* effectFile = obs_module_file("displacement.effect"); + char* errorMessage = nullptr; + this->customEffect = gs_effect_create_from_file(effectFile, &errorMessage); + bfree(effectFile); + if (errorMessage != nullptr) { + PLOG_ERROR("%s", errorMessage); + bfree(errorMessage); + } + obs_leave_graphics(); + + update(data); +} + +Filter::Displacement::Instance::~Instance() { + obs_enter_graphics(); + if (customEffect) + gs_effect_destroy(customEffect); + if (dispmap.texture) + gs_texture_destroy(dispmap.texture); + obs_leave_graphics(); +} + +void Filter::Displacement::Instance::update(obs_data_t *data) { + updateDisplacementMap(obs_data_get_string(data, "file")); + + distance = float_t(obs_data_get_double(data, "ratio")); + vec2_set(&displacementScale, + float_t(obs_data_get_double(data, "scale")), + float_t(obs_data_get_double(data, "scale"))); +} + +uint32_t Filter::Displacement::Instance::get_width() { + return 0; +} + +uint32_t Filter::Displacement::Instance::get_height() { + return 0; +} + +void Filter::Displacement::Instance::activate() {} + +void Filter::Displacement::Instance::deactivate() {} + +void Filter::Displacement::Instance::show() {} + +void Filter::Displacement::Instance::hide() {} + +void Filter::Displacement::Instance::video_tick(float time) { + timer += time; + if (timer >= 1.0) { + timer -= 1.0; + updateDisplacementMap(dispmap.file); + } +} + +float interp(float a, float b, float v) { + return (a * (1.0f - v)) + (b * v); +} + +void Filter::Displacement::Instance::video_render(gs_effect_t *) { + obs_source_t *parent = obs_filter_get_parent(context); + obs_source_t *target = obs_filter_get_target(context); + uint32_t + baseW = obs_source_get_base_width(target), + baseH = obs_source_get_base_height(target); + + // Skip rendering if our target, parent or context is not valid. + if (!target || !parent || !context || !dispmap.texture) { + obs_source_skip_video_filter(context); + return; + } + + if (!obs_source_process_filter_begin(context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) + return; + + gs_eparam_t *param; + + vec2 texelScale; + vec2_set(&texelScale, + interp((1.0f / baseW), 1.0f, distance), + interp((1.0f / baseH), 1.0f, distance)); + param = gs_effect_get_param_by_name(customEffect, "texelScale"); + if (param) + gs_effect_set_vec2(param, &texelScale); + else + PLOG_ERROR("Failed to set texel scale param."); + + param = gs_effect_get_param_by_name(customEffect, "displacementScale"); + if (param) + gs_effect_set_vec2(param, &displacementScale); + else + PLOG_ERROR("Failed to set displacement scale param."); + + param = gs_effect_get_param_by_name(customEffect, "displacementMap"); + if (param) + gs_effect_set_texture(param, dispmap.texture); + else + PLOG_ERROR("Failed to set texture param."); + + obs_source_process_filter_end(context, customEffect, baseW, baseH); +} + +std::string Filter::Displacement::Instance::get_file() { + return dispmap.file; +} + +void Filter::Displacement::Instance::updateDisplacementMap(std::string file) { + bool shouldUpdateTexture = false; + + // Different File + if (file != dispmap.file) { + dispmap.file = file; + shouldUpdateTexture = true; + } else { // Different Timestamps + struct stat stats; + if (os_stat(dispmap.file.c_str(), &stats) != 0) { + shouldUpdateTexture = shouldUpdateTexture || + (dispmap.createTime != stats.st_ctime) || + (dispmap.modifiedTime != stats.st_mtime); + dispmap.createTime = stats.st_ctime; + dispmap.modifiedTime = stats.st_mtime; + } + } + + if (shouldUpdateTexture) { + obs_enter_graphics(); + if (dispmap.texture) { + gs_texture_destroy(dispmap.texture); + dispmap.texture = nullptr; + } + if (os_file_exists(file.c_str())) + dispmap.texture = gs_texture_create_from_file(dispmap.file.c_str()); + obs_leave_graphics(); + } +} diff --git a/source/filter-displacement.h b/source/filter-displacement.h new file mode 100644 index 0000000..47351ba --- /dev/null +++ b/source/filter-displacement.h @@ -0,0 +1,88 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include "plugin.h" +#include +#include +#include + +namespace Filter { + class Displacement { + public: + Displacement(); + ~Displacement(); + + static const char *get_name(void *); + + static void *create(obs_data_t *, obs_source_t *); + static void destroy(void *); + static uint32_t get_width(void *); + static uint32_t get_height(void *); + static void get_defaults(obs_data_t *); + static obs_properties_t *get_properties(void *); + static void update(void *, obs_data_t *); + static void activate(void *); + static void deactivate(void *); + static void show(void *); + static void hide(void *); + static void video_tick(void *, float); + static void video_render(void *, gs_effect_t *); + + private: + obs_source_info sourceInfo; + + private: + class Instance { + public: + Instance(obs_data_t*, obs_source_t*); + ~Instance(); + + void update(obs_data_t*); + uint32_t get_width(); + uint32_t get_height(); + void activate(); + void deactivate(); + void show(); + void hide(); + void video_tick(float); + void video_render(gs_effect_t*); + + std::string get_file(); + + private: + void updateDisplacementMap(std::string file); + + obs_source_t *context; + gs_effect_t *customEffect; + float_t distance; + vec2 displacementScale; + struct { + std::string file; + + gs_texture_t* texture; + time_t createTime, + modifiedTime; + size_t size; + } dispmap; + + float_t timer; + }; + }; +} \ No newline at end of file diff --git a/source/filter-shape.cpp b/source/filter-shape.cpp new file mode 100644 index 0000000..4d15227 --- /dev/null +++ b/source/filter-shape.cpp @@ -0,0 +1,357 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "filter-shape.h" +#include "strings.h" + +#include "libobs/util/platform.h" +#include "libobs/graphics/graphics.h" +#include "libobs/graphics/matrix4.h" + +#include +#include +#include +#include + +typedef std::pair cacheKey; +typedef std::pair cacheValue; +static std::map cache; +static const uint32_t minimumPoints = 3; +static const uint32_t maximumPoints = 16; + +static void initialize() { + if (cache.size() != 0) + return; + for (uint32_t point = 0; point < maximumPoints; point++) { + std::vector handle(1024), name(1024); + const char* vals[] = { + P_SHAPE_POINT_X, + P_SHAPE_POINT_Y, + P_SHAPE_POINT_U, + P_SHAPE_POINT_V + }; + for (const char* v : vals) { + snprintf(handle.data(), handle.size(), "%s.%" PRIu32, v, point); + snprintf(name.data(), name.size(), P_TRANSLATE(v), point); + cacheValue x = std::make_pair(std::string(handle.data()), std::string(name.data())); + cache.insert(std::make_pair(std::make_pair(point, v), x)); + } + } +} + +Filter::Shape::Shape() { + memset(&sourceInfo, 0, sizeof(obs_source_info)); + sourceInfo.id = "obs-stream-effects-shape"; + sourceInfo.type = OBS_SOURCE_TYPE_FILTER; + sourceInfo.output_flags = OBS_SOURCE_VIDEO; + sourceInfo.get_name = get_name; + sourceInfo.get_defaults = get_defaults; + sourceInfo.get_properties = get_properties; + + sourceInfo.create = create; + sourceInfo.destroy = destroy; + sourceInfo.update = update; + sourceInfo.activate = activate; + sourceInfo.deactivate = deactivate; + sourceInfo.show = show; + sourceInfo.hide = hide; + sourceInfo.video_tick = video_tick; + sourceInfo.video_render = video_render; + + obs_register_source(&sourceInfo); + + initialize(); +} + +Filter::Shape::~Shape() { + +} + +const char * Filter::Shape::get_name(void *) { + return "Shape"; +} + +void Filter::Shape::get_defaults(obs_data_t *data) { + obs_data_set_default_bool(data, P_SHAPE_LOOP, true); + obs_data_set_default_int(data, P_SHAPE_POINTS, minimumPoints); + + for (uint32_t point = 0; point < maximumPoints; point++) { + const char* vals[] = { + P_SHAPE_POINT_X, + P_SHAPE_POINT_Y, + P_SHAPE_POINT_U, + P_SHAPE_POINT_V + }; + for (const char* v : vals) { + auto strings = cache.find(std::make_pair(point, v)); + if (strings != cache.end()) { + obs_data_set_default_double(data, strings->second.first.c_str(), 0); + } + } + } +} + +obs_properties_t * Filter::Shape::get_properties(void *) { + obs_properties_t *pr = obs_properties_create(); + obs_property_t* p = NULL; + + p = obs_properties_add_bool(pr, P_SHAPE_LOOP, P_TRANSLATE(P_SHAPE_LOOP)); + obs_property_set_long_description(p, P_DESC(P_SHAPE_LOOP)); + + p = obs_properties_add_int_slider(pr, P_SHAPE_POINTS, P_TRANSLATE(P_SHAPE_POINTS), minimumPoints, maximumPoints, 1); + obs_property_set_long_description(p, P_DESC(P_SHAPE_POINTS)); + obs_property_set_modified_callback(p, modified_properties); + + for (uint32_t point = 0; point < maximumPoints; point++) { + std::pair vals[] = { + { P_SHAPE_POINT_X, P_TRANSLATE(P_DESC(P_SHAPE_POINT_X)) }, + { P_SHAPE_POINT_Y, P_TRANSLATE(P_DESC(P_SHAPE_POINT_Y)) }, + { P_SHAPE_POINT_U, P_TRANSLATE(P_DESC(P_SHAPE_POINT_U)) }, + { P_SHAPE_POINT_V, P_TRANSLATE(P_DESC(P_SHAPE_POINT_V)) } + }; + for (std::pair v : vals) { + auto strings = cache.find(std::make_pair(point, v.first)); + if (strings != cache.end()) { + p = obs_properties_add_float_slider(pr, strings->second.first.c_str(), strings->second.second.c_str(), 0, 100.0, 0.01); + obs_property_set_long_description(p, v.second); + } + } + } + + return pr; +} + +bool Filter::Shape::modified_properties(obs_properties_t *pr, obs_property_t *, obs_data_t *data) { + uint32_t points = (uint32_t)obs_data_get_int(data, P_SHAPE_POINTS); + for (uint32_t point = 0; point < maximumPoints; point++) { + bool visible = point < points ? true : false; + const char* vals[] = { + P_SHAPE_POINT_X, + P_SHAPE_POINT_Y, + P_SHAPE_POINT_U, + P_SHAPE_POINT_V + }; + for (const char* v : vals) { + auto strings = cache.find(std::make_pair(point, v)); + if (strings != cache.end()) { + obs_property_set_visible(obs_properties_get(pr, strings->second.first.c_str()), visible); + } + } + } + return true; +} + +void * Filter::Shape::create(obs_data_t *data, obs_source_t *source) { + return new Instance(data, source); +} + +void Filter::Shape::destroy(void *ptr) { + delete reinterpret_cast(ptr); +} + +uint32_t Filter::Shape::get_width(void *ptr) { + return reinterpret_cast(ptr)->get_width(); +} + +uint32_t Filter::Shape::get_height(void *ptr) { + return reinterpret_cast(ptr)->get_height(); +} + +void Filter::Shape::update(void *ptr, obs_data_t *data) { + reinterpret_cast(ptr)->update(data); +} + +void Filter::Shape::activate(void *ptr) { + reinterpret_cast(ptr)->activate(); +} + +void Filter::Shape::deactivate(void *ptr) { + reinterpret_cast(ptr)->deactivate(); +} + +void Filter::Shape::show(void *ptr) { + reinterpret_cast(ptr)->show(); +} + +void Filter::Shape::hide(void *ptr) { + reinterpret_cast(ptr)->hide(); +} + +void Filter::Shape::video_tick(void *ptr, float time) { + reinterpret_cast(ptr)->video_tick(time); +} + +void Filter::Shape::video_render(void *ptr, gs_effect_t *effect) { + reinterpret_cast(ptr)->video_render(effect); +} + +Filter::Shape::Instance::Instance(obs_data_t *data, obs_source_t *context) + : context(context) { + obs_enter_graphics(); + m_vertexHelper = new Helper::VertexBuffer(); + m_vertexHelper->set_uv_layers(1); + obs_leave_graphics(); + + update(data); +} + +Filter::Shape::Instance::~Instance() { + obs_enter_graphics(); + delete m_vertexHelper; + obs_leave_graphics(); +} + +void Filter::Shape::Instance::update(obs_data_t *data) { + uint32_t points = (uint32_t)obs_data_get_int(data, P_SHAPE_POINTS); + m_vertexHelper->resize(points); + for (uint32_t point = 0; point < points; point++) { + Helper::Vertex& v = m_vertexHelper->at(point); + { + auto strings = cache.find(std::make_pair(point, P_SHAPE_POINT_X)); + if (strings != cache.end()) { + v.position.x = obs_data_get_double(data, strings->second.first.c_str()) / 100.0; + } + } + { + auto strings = cache.find(std::make_pair(point, P_SHAPE_POINT_Y)); + if (strings != cache.end()) { + v.position.y = obs_data_get_double(data, strings->second.first.c_str()) / 100.0; + } + } + { + auto strings = cache.find(std::make_pair(point, P_SHAPE_POINT_U)); + if (strings != cache.end()) { + v.uv[0].x = obs_data_get_double(data, strings->second.first.c_str()) / 100.0; + } + } + { + auto strings = cache.find(std::make_pair(point, P_SHAPE_POINT_V)); + if (strings != cache.end()) { + v.uv[0].y = obs_data_get_double(data, strings->second.first.c_str()) / 100.0; + } + } + v.color = 0xFFFFFFFF; + v.position.z = 10.0f; + } + obs_enter_graphics(); + m_vertexBuffer = m_vertexHelper->update(); + m_texRender = gs_texrender_create(GS_RGBA, GS_Z32F); + obs_leave_graphics(); +} + +uint32_t Filter::Shape::Instance::get_width() { + return 0; +} + +uint32_t Filter::Shape::Instance::get_height() { + return 0; +} + +void Filter::Shape::Instance::activate() { + +} + +void Filter::Shape::Instance::deactivate() { + +} + +void Filter::Shape::Instance::show() { + +} + +void Filter::Shape::Instance::hide() { + +} + +void Filter::Shape::Instance::video_tick(float) { + +} + +void Filter::Shape::Instance::video_render(gs_effect_t *effect) { + obs_source_t *parent = obs_filter_get_parent(context); + obs_source_t *target = obs_filter_get_target(context); + uint32_t + baseW = obs_source_get_base_width(target), + baseH = obs_source_get_base_height(target); + + // Skip rendering if our target, parent or context is not valid. + if (!target || !parent || !context) { + obs_source_skip_video_filter(context); + return; + } + + gs_texrender_reset(m_texRender); + if (gs_texrender_begin(m_texRender, baseW, baseH)) { + if (obs_source_process_filter_begin(context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) { + obs_source_process_filter_end(context, obs_get_base_effect(OBS_EFFECT_OPAQUE), baseW, baseH); + } else { + obs_source_skip_video_filter(context); + } + gs_texrender_end(m_texRender); + } else { + obs_source_skip_video_filter(context); + } + gs_texture* tex = gs_texrender_get_texture(m_texRender); + + gs_projection_push(); + gs_viewport_push(); + + matrix4 alignedMatrix; + gs_matrix_get(&alignedMatrix); + gs_matrix_push(); + gs_matrix_set(&alignedMatrix); + gs_matrix_scale3f(baseW, baseH, 1.0); + + gs_set_cull_mode(GS_NEITHER); + gs_enable_blending(false); + gs_enable_depth_test(false); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_enable_color(true, true, true, true); + gs_enable_depth_test(false); + + gs_effect_t* eff = obs_get_base_effect(OBS_EFFECT_SOLID); + while (gs_effect_loop(eff, "Solid")) { + //gs_effect_set_texture(gs_effect_get_param_by_name(eff, "image"), tex); + //gs_draw_sprite(tex, 0, baseW * m_vertexHelper->at(0).position.x, baseH * m_vertexHelper->at(0).position.y); + + //gs_load_default_samplerstate(false, 0); + gs_load_vertexbuffer(m_vertexBuffer); + gs_load_indexbuffer(nullptr); + gs_draw(GS_LINESTRIP, 0, 3); + + gs_render_start(true); + gs_vertex2f(0, 0); + gs_color(0xFFFFFFFF); + + gs_vertex2f(1, 0); + gs_color(0xFFFFFFFF); + + gs_vertex2f(1, 1); + gs_color(0xFFFFFFFF); + + gs_vertex2f(0, 1); + gs_color(0xFFFFFFFF); + gs_render_stop(GS_LINESTRIP); + } + + gs_matrix_pop(); + gs_viewport_pop(); + gs_projection_pop(); +} diff --git a/source/filter-shape.h b/source/filter-shape.h new file mode 100644 index 0000000..53a4bd4 --- /dev/null +++ b/source/filter-shape.h @@ -0,0 +1,84 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include "plugin.h" +#include "gs-helper.h" + +#define P_SHAPE "Shape" +#define P_SHAPE_LOOP "Shape.Loop" +#define P_SHAPE_POINTS "Shape.Points" +#define P_SHAPE_POINT_X "Shape.Point.X" +#define P_SHAPE_POINT_Y "Shape.Point.Y" +#define P_SHAPE_POINT_U "Shape.Point.U" +#define P_SHAPE_POINT_V "Shape.Point.V" + +namespace Filter { + class Shape { + public: + Shape(); + ~Shape(); + + static const char *get_name(void *); + static void get_defaults(obs_data_t *); + static obs_properties_t *get_properties(void *); + static bool modified_properties(obs_properties_t *, obs_property_t *, obs_data_t *); + + static void *create(obs_data_t *, obs_source_t *); + static void destroy(void *); + static uint32_t get_width(void *); + static uint32_t get_height(void *); + static void update(void *, obs_data_t *); + static void activate(void *); + static void deactivate(void *); + static void show(void *); + static void hide(void *); + static void video_tick(void *, float); + static void video_render(void *, gs_effect_t *); + + private: + obs_source_info sourceInfo; + + private: + class Instance { + public: + Instance(obs_data_t*, obs_source_t*); + ~Instance(); + + void update(obs_data_t*); + uint32_t get_width(); + uint32_t get_height(); + void activate(); + void deactivate(); + void show(); + void hide(); + void video_tick(float); + void video_render(gs_effect_t*); + + private: + obs_source_t *context; + gs_effect_t *customEffect; + Helper::VertexBuffer *m_vertexHelper; + gs_vertbuffer_t *m_vertexBuffer; + + size_t width, height; + gs_texrender_t *m_texRender; + }; + }; +} diff --git a/source/gs-helper.cpp b/source/gs-helper.cpp new file mode 100644 index 0000000..669009d --- /dev/null +++ b/source/gs-helper.cpp @@ -0,0 +1,111 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gs-helper.h" + +Helper::VertexBuffer::VertexBuffer() { + m_positions.resize(1); + m_normals.resize(1); + m_tangents.resize(1); + m_colors.resize(1); + m_uvs.resize(1); + m_uvs[0].resize(1); + m_uvArrays.resize(1); + m_uvArrays[0].width = 2; + m_uvArrays[0].array = m_uvs[0].data(); + + m_vertexData = gs_vbdata_create(); + m_vertexData->num = 1; + m_vertexData->points = m_positions.data(); + m_vertexData->normals = m_normals.data(); + m_vertexData->tangents = m_normals.data(); + m_vertexData->colors = m_colors.data(); + m_vertexData->num_tex = m_uvs.size(); + m_vertexData->tvarray = m_uvArrays.data(); + + m_vertexBuffer = gs_vertexbuffer_create(m_vertexData, GS_DYNAMIC); +} + +Helper::VertexBuffer::~VertexBuffer() { + std::memset(m_vertexData, 0, sizeof(gs_vb_data)); + gs_vertexbuffer_destroy(m_vertexBuffer); +} + +void Helper::VertexBuffer::set_uv_layers(size_t count) { + size_t realcount = count > 8 ? 8 : count; + m_uvs.resize(realcount); +} + +size_t Helper::VertexBuffer::get_uv_layers() { + return m_uvs.size(); +} + +gs_vertbuffer_t* Helper::VertexBuffer::update() { + // Update Buffers + size_t verts = this->size(); + m_positions.resize(verts); + m_normals.resize(verts); + m_tangents.resize(verts); + m_colors.resize(verts); + for (size_t layer = 0; layer < m_uvs.size(); layer++) { + m_uvs[layer].resize(verts); + m_uvArrays[layer].width = 2; + m_uvArrays[layer].array = m_uvs[layer].data(); + } + + // Prepare Vertex Data + for (size_t vert = 0; vert < verts; vert++) { + Vertex& v = this->at(vert); + + vec3_copy(&m_positions[vert], &v.position); + vec3_copy(&m_normals[vert], &v.normal); + vec3_copy(&m_tangents[vert], &v.tangent); + m_colors[vert] = v.color; + for (size_t layer = 0; layer < m_uvs.size(); layer++) { + vec2_copy(&m_uvs[layer][vert], &v.uv[layer]); + } + } + + // Upload to GPU + m_vertexData->num = verts; + m_vertexData->points = m_positions.data(); + m_vertexData->normals = m_normals.data(); + m_vertexData->tangents = m_normals.data(); + m_vertexData->colors = m_colors.data(); + m_vertexData->num_tex = m_uvArrays.size(); + m_vertexData->tvarray = m_uvArrays.data(); + gs_vertexbuffer_flush(m_vertexBuffer); + return m_vertexBuffer; +} + +Helper::Vertex::Vertex() { + position.x = + position.y = + position.z = + normal.x = + normal.y = + normal.z = + tangent.x = + tangent.y = + tangent.z = 0; + color = 0; + for (size_t idx = 0; idx < 8; idx++) { + uv[idx].x = uv[idx].y = 0; + } +} diff --git a/source/gs-helper.h b/source/gs-helper.h new file mode 100644 index 0000000..a56f5ae --- /dev/null +++ b/source/gs-helper.h @@ -0,0 +1,58 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include "plugin.h" + +#include "libobs/graphics/graphics.h" + +#include + +namespace Helper { + struct Vertex { + Vertex(); + + vec3 + position, + normal, + tangent; + vec2 uv[8]; + uint32_t + color; + }; + + class VertexBuffer : public std::vector { + public: + VertexBuffer(); + virtual ~VertexBuffer(); + + void set_uv_layers(size_t count); + size_t get_uv_layers(); + + gs_vertbuffer_t* update(); + + private: + gs_vb_data *m_vertexData; + gs_vertbuffer_t *m_vertexBuffer; + std::vector m_uvArrays; + std::vector m_positions, m_normals, m_tangents; + std::vector m_colors; + std::vector> m_uvs; + }; +} \ No newline at end of file diff --git a/source/plugin.cpp b/source/plugin.cpp new file mode 100644 index 0000000..f53bd1f --- /dev/null +++ b/source/plugin.cpp @@ -0,0 +1,61 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "plugin.h" +#include "filter-displacement.h" +#include "filter-shape.h" + +OBS_DECLARE_MODULE(); +OBS_MODULE_AUTHOR("Michael Fabian Dirks"); +OBS_MODULE_USE_DEFAULT_LOCALE("obs-stream-effects", "en-US"); + +Filter::Displacement *filterDisplacement; +Filter::Shape *filterShape; + +MODULE_EXPORT bool obs_module_load(void) { + filterDisplacement = new Filter::Displacement(); + filterShape = new Filter::Shape(); + + return true; +} + +MODULE_EXPORT void obs_module_unload(void) { + delete filterShape; + delete filterDisplacement; +} + +MODULE_EXPORT const char* obs_module_name() { + return PLUGIN_NAME; +} + +MODULE_EXPORT const char* obs_module_description() { + return PLUGIN_NAME; +} + +#ifdef _WIN32 +#define NOMINMAX +#define NOINOUT + +#include + +BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { + return TRUE; +} +#endif + diff --git a/source/plugin.h b/source/plugin.h new file mode 100644 index 0000000..f4002ab --- /dev/null +++ b/source/plugin.h @@ -0,0 +1,59 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include + +#pragma warning (push) +#pragma warning (disable: 4201) +#include "libobs/obs-module.h" +#include "libobs/util/platform.h" +#pragma warning (pop) + +// Plugin +#define PLUGIN_NAME "Stream Effects" +#include "version.h" + +#define PLOG(level, ...) blog(level, "[" PLUGIN_NAME "] " __VA_ARGS__); +#define PLOG_ERROR(...) PLOG(LOG_ERROR, __VA_ARGS__) +#define PLOG_WARNING(...) PLOG(LOG_WARNING, __VA_ARGS__) +#define PLOG_INFO(...) PLOG(LOG_INFO, __VA_ARGS__) +#define PLOG_DEBUG(...) PLOG(LOG_DEBUG, __VA_ARGS__) + +// Utility +#define vstr(s) dstr(s) +#define dstr(s) #s + +#define clamp(val,low,high) (val > high ? high : (val < low ? low : val)) +#ifdef max +#undef max +#endif +#define max(val,high) (val > high ? val : high) +#ifdef min +#undef min +#endif +#define min(val,low ) (val < low ? val : low) + +#ifndef __FUNCTION_NAME__ +#if defined(_WIN32) || defined(_WIN64) //WINDOWS +#define __FUNCTION_NAME__ __FUNCTION__ +#else //*NIX +#define __FUNCTION_NAME__ __func__ +#endif +#endif diff --git a/source/strings.h b/source/strings.h new file mode 100644 index 0000000..e7ef0b8 --- /dev/null +++ b/source/strings.h @@ -0,0 +1,25 @@ +/* + * Modern effects for a modern Streamer + * Copyright (C) 2016 - 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include "plugin.h" + +#define P_TRANSLATE(x) obs_module_text(x) +#define P_DESC(x) x ".Description" + diff --git a/source/version.h.in b/source/version.h.in new file mode 100644 index 0000000..965e361 --- /dev/null +++ b/source/version.h.in @@ -0,0 +1,25 @@ +/* + * A Plugin that integrates the AMD AMF encoder into OBS Studio + * Copyright (C) 2016 - 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once + +const uint16_t PLUGIN_VERSION_MAJOR = @VERSION_MAJOR@; +const uint16_t PLUGIN_VERSION_MINOR = @VERSION_MINOR@; +const uint32_t PLUGIN_VERSION_PATCH = @VERSION_PATCH@; +const uint64_t PLUGIN_VERSION_FULL = (((uint64_t)(PLUGIN_VERSION_MAJOR & 0xFFFF) << 48ull) | ((uint64_t)(PLUGIN_VERSION_MINOR & 0xFFFF) << 32ull) | ((uint64_t)(PLUGIN_VERSION_PATCH) & 0xFFFFFFFF));