gs_device::gs_device(gs_init_data *data) : curRenderTarget (NULL), curZStencilBuffer (NULL), curRenderSide (0), curIndexBuffer (NULL), curVertexBuffer (NULL), curVertexShader (NULL), curPixelShader (NULL), curSwapChain (&defaultSwap), zstencilStateChanged (true), rasterStateChanged (true), blendStateChanged (true), curDepthStencilState (NULL), curRasterState (NULL), curBlendState (NULL), curToplogy (D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) { ComPtr<IDXGIAdapter1> adapter; matrix4_identity(&curProjMatrix); matrix4_identity(&curViewMatrix); matrix4_identity(&curViewProjMatrix); memset(&viewport, 0, sizeof(viewport)); for (size_t i = 0; i < GS_MAX_TEXTURES; i++) { curTextures[i] = NULL; curSamplers[i] = NULL; } InitFactory(data->adapter, adapter.Assign()); InitDevice(data, adapter); device_set_render_target(this, NULL, NULL); }
Matrix4 testMatrix4MultipliedConstant2(const Matrix4& a) { return matrix4_for_expression( matrix4_multiplied( matrix4_identity(a), matrix4_identity(g_matrix4_identity) ) ); }
Matrix4 testMatrix4AffineMultiplied2(const Matrix4& a, const Matrix4& b) { return matrix4_affine_for_expression( matrix4_multiplied( matrix4_identity(a), matrix4_identity(b) ) ); }
/** * @ingroup matrix4 * @brief Multiply a matrix by a matrix, returns the matrix changed * * @param self the matrix being changed * @param mT The matrix being multiplied into self * * self = self * mT */ HYPAPI matrix4 * matrix4_multiply(matrix4 *self, const matrix4 *mT) { /* mT is the multiplicand */ matrix4 r; matrix4_identity(&r); /* first row */ r.r00 = self->c00 * mT->c00 + self->c01 * mT->c10 + self->c02 * mT->c20 + self->c03 * mT->c30; r.r01 = self->c10 * mT->c00 + self->c11 * mT->c10 + self->c12 * mT->c20 + self->c13 * mT->c30; r.r02 = self->c20 * mT->c00 + self->c21 * mT->c10 + self->c22 * mT->c20 + self->c23 * mT->c30; r.r03 = self->c30 * mT->c00 + self->c31 * mT->c10 + self->c32 * mT->c20 + self->c33 * mT->c30; /* second row */ r.r10 = self->c00 * mT->c01 + self->c01 * mT->c11 + self->c02 * mT->c21 + self->c03 * mT->c31; r.r11 = self->c10 * mT->c01 + self->c11 * mT->c11 + self->c12 * mT->c21 + self->c13 * mT->c31; r.r12 = self->c20 * mT->c01 + self->c21 * mT->c11 + self->c22 * mT->c21 + self->c23 * mT->c31; r.r13 = self->c30 * mT->c01 + self->c31 * mT->c11 + self->c32 * mT->c21 + self->c33 * mT->c31; /* third row */ r.r20 = self->c00 * mT->c02 + self->c01 * mT->c12 + self->c02 * mT->c22 + self->c03 * mT->c32; r.r21 = self->c10 * mT->c02 + self->c11 * mT->c12 + self->c12 * mT->c22 + self->c13 * mT->c32; r.r22 = self->c20 * mT->c02 + self->c21 * mT->c12 + self->c22 * mT->c22 + self->c23 * mT->c32; r.r23 = self->c30 * mT->c02 + self->c31 * mT->c12 + self->c32 * mT->c22 + self->c33 * mT->c32; /* fourth row */ r.r30 = self->c00 * mT->c03 + self->c01 * mT->c13 + self->c02 * mT->c23 + self->c03 * mT->c33; r.r31 = self->c10 * mT->c03 + self->c11 * mT->c13 + self->c12 * mT->c23 + self->c13 * mT->c33; r.r32 = self->c20 * mT->c03 + self->c21 * mT->c13 + self->c22 * mT->c23 + self->c23 * mT->c33; r.c33 = self->c30 * mT->c03 + self->c31 * mT->c13 + self->c32 * mT->c23 + self->c33 * mT->c33; matrix4_set(self, &r); /* overwrite/save it */ return self; }
/** * @ingroup matrix4 * @brief creates a translation matrix. It's opinionated about what that means. * */ HYPAPI matrix4 * matrix4_make_transformation_translationv3(matrix4 *self, const vector3 *translation) { matrix4_identity(self); /* assuming col-major */ self->m[12] = translation->x; self->m[13] = translation->y; self->m[14] = translation->z; return self; }
/** * @ingroup matrix4 * @brief creates a scaling matrix. It's opinionated about what that means. * */ HYPAPI matrix4 * matrix4_make_transformation_scalingv3(matrix4 *self, const vector3 *scale) { matrix4_identity(self); /* assuming col-major */ self->m[0] = scale->x; self->m[5] = scale->y; self->m[10] = scale->z; return self; }
Vector3 testMulti2(const Matrix4& a, const Vector3& b, const Vector3& c) { return vector3_for_expression( vector_added( point_multiplied( vector3_identity(b), matrix_transposed(matrix4_identity(a)) ), vector3_identity(c) ) ); }
Vector3 testMatrixMultiplied2(const Vector3& a, const Matrix4& b) { return vector3_for_expression( point_multiplied( vector_added( vector3_identity(a), vector3_literal(Vector3(1, 0, 0)) ), matrix4_identity(b) ) ); }
void OBSBasicPreview::GetStretchHandleData(const vec2 &pos) { OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow()); OBSScene scene = main->GetCurrentScene(); if (!scene) return; HandleFindData data(pos, main->previewScale); obs_scene_enum_items(scene, FindHandleAtPos, &data); stretchItem = std::move(data.item); stretchHandle = data.handle; if (stretchHandle != ItemHandle::None) { matrix4 boxTransform; vec3 itemUL; float itemRot; stretchItemSize = GetItemSize(stretchItem); obs_sceneitem_get_box_transform(stretchItem, &boxTransform); itemRot = obs_sceneitem_getrot(stretchItem); vec3_from_vec4(&itemUL, &boxTransform.t); /* build the item space conversion matrices */ matrix4_identity(&itemToScreen); matrix4_rotate_aa4f(&itemToScreen, &itemToScreen, 0.0f, 0.0f, 1.0f, RAD(itemRot)); matrix4_translate3f(&itemToScreen, &itemToScreen, itemUL.x, itemUL.y, 0.0f); matrix4_identity(&screenToItem); matrix4_translate3f(&screenToItem, &screenToItem, -itemUL.x, -itemUL.y, 0.0f); matrix4_rotate_aa4f(&screenToItem, &screenToItem, 0.0f, 0.0f, 1.0f, RAD(-itemRot)); } }
gs_device::gs_device(const gs_init_data *data) : curSwapChain (&defaultSwap), curToplogy (D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) { ComPtr<IDXGIAdapter1> adapter; matrix4_identity(&curProjMatrix); matrix4_identity(&curViewMatrix); matrix4_identity(&curViewProjMatrix); memset(&viewport, 0, sizeof(viewport)); for (size_t i = 0; i < GS_MAX_TEXTURES; i++) { curTextures[i] = NULL; curSamplers[i] = NULL; } InitCompiler(); InitFactory(data->adapter, adapter.Assign()); InitDevice(data, adapter); device_set_render_target(this, NULL, NULL); }
/** * @ingroup matrix4 * @brief creates a rotation matrix about the z. It's opinionated about what that means. * * multiply this matrix by another matrix to rotate the other matrix */ HYPAPI matrix4 * matrix4_make_transformation_rotationf_z(matrix4 *m, float angle) { float c = HYP_COS(angle); float s = HYP_SIN(angle); matrix4_identity(m); /* assuming col-major */ m->r00 = c; m->r01 = s; m->r10 = -s; m->r11 = c; return m; }
static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb, int cx, int cy) { if (!vb) return; matrix4 transform; matrix4_identity(&transform); transform.x.x = cx; transform.y.y = cy; gs_load_vertexbuffer(vb); gs_matrix_push(); gs_matrix_mul(&transform); while (gs_effect_loop(effect, "Solid")) gs_draw(GS_LINESTRIP, 0, 0); gs_matrix_pop(); }
/** * @ingroup matrix4 * @brief converts the quaternion to a 4x4 rotation matrix (column major, right hand rule) * */ HYPAPI matrix4 * matrix4_make_transformation_rotationq(matrix4 *self, const quaternion *qT) { matrix4 *m; const quaternion *q; q = qT; m = self; matrix4_identity(m); m->m[0] = 1.0f - 2.0f * (q->y * q->y + q->z * q->z); m->m[4] = 2.0f * (q->x * q->y - q->z * q->w); m->m[8] = 2.0f * (q->x * q->z + q->y * q->w); m->m[1] = 2.0f * (q->x * q->y + q->z * q->w); m->m[5] = 1.0f - 2.0f * (q->x * q->x + q->z * q->z); m->m[9] = 2.0f * (q->y * q->z - q->x * q->w); m->m[2] = 2.0f * (q->x * q->z - q->y * q->w); m->m[6] = 2.0f * (q->y * q->z + q->x * q->w); m->m[10] = 1.0f - 2.0f * (q->x * q->x + q->y * q->y); return self; }
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = (OBSProjector *)data; if (updatingMultiview || !window->ready) return; OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window(); uint32_t targetCX, targetCY; int x, y; float scale; targetCX = (uint32_t)window->fw; targetCY = (uint32_t)window->fh; GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); OBSSource previewSrc = main->GetCurrentSceneSource(); OBSSource programSrc = main->GetProgramSource(); bool studioMode = main->IsPreviewProgramMode(); auto renderVB = [&](gs_vertbuffer_t *vb, int cx, int cy, uint32_t colorVal) { if (!vb) return; matrix4 transform; matrix4_identity(&transform); transform.x.x = cx; transform.y.y = cy; gs_load_vertexbuffer(vb); gs_matrix_push(); gs_matrix_mul(&transform); gs_effect_set_color(window->color, colorVal); while (gs_effect_loop(window->solid, "Solid")) gs_draw(GS_LINESTRIP, 0, 0); gs_matrix_pop(); }; auto drawBox = [&](float cx, float cy, uint32_t colorVal) { gs_effect_set_color(window->color, colorVal); while (gs_effect_loop(window->solid, "Solid")) gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy); }; auto setRegion = [&](float bx, float by, float cx, float cy) { float vX = int(x + bx * scale); float vY = int(y + by * scale); float vCX = int(cx * scale); float vCY = int(cy * scale); float oL = bx; float oT = by; float oR = (bx + cx); float oB = (by + cy); startRegion(vX, vY, vCX, vCY, oL, oR, oT, oB); }; auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX += window->scenesCX; break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX = window->scenesCX; break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } window->siX = window->sourceX + window->thickness; window->siY = window->sourceY + window->thickness; }; auto calcPreviewProgram = [&](bool program) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = window->thickness + window->pvwprgCX / 2; window->sourceY = window->thickness; window->labelX = window->offset + window->pvwprgCX / 2; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = window->pvwprgCX + window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->pvwprgCX + window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } } }; auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) { gs_matrix_push(); gs_matrix_translate3f(tx, ty, 0.0f); drawBox(cx, cy, color); gs_matrix_pop(); }; // Define the whole usable region for the multiview startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, window->fw, 0.0f, window->fh); // Change the background color to highlight all sources drawBox(window->fw, window->fh, outerColor); /* ----------------------------- */ /* draw sources */ for (size_t i = 0; i < maxSrcs; i++) { // Handle all the offsets calcBaseSource(i); if (i >= numSrcs) { // Just paint the background and continue paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, outerColor); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); continue; } OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]); // We have a source. Now chose the proper highlight color uint32_t colorVal = outerColor; if (src == programSrc) colorVal = programColor; else if (src == previewSrc) colorVal = studioMode ? previewColor : programColor; // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, colorVal); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); /* ----------- */ // Render the source gs_matrix_push(); gs_matrix_translate3f(window->siX, window->siY, 0.0f); gs_matrix_scale3f(window->siScaleX, window->siScaleY, 1.0f); setRegion(window->siX, window->siY, window->siCX, window->siCY); obs_source_video_render(src); endRegion(); gs_matrix_pop(); /* ----------- */ // Render the label if (!drawLabel) continue; obs_source *label = window->multiviewLabels[i + 2]; if (!label) continue; window->offset = labelOffset(label, window->scenesCX); gs_matrix_push(); gs_matrix_translate3f(window->sourceX + window->offset, (window->scenesCY * 0.85f) + window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(label), obs_source_get_height(label) + int(window->sourceY * 0.015f), labelColor); obs_source_video_render(label); gs_matrix_pop(); } /* ----------------------------- */ /* draw preview */ obs_source_t *previewLabel = window->multiviewLabels[0]; window->offset = labelOffset(previewLabel, window->pvwprgCX); calcPreviewProgram(false); // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the preview gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); if (studioMode) obs_source_video_render(previewSrc); else obs_render_main_texture(); if (drawSafeArea) { renderVB(window->actionSafeMargin, targetCX, targetCY, outerColor); renderVB(window->graphicsSafeMargin, targetCX, targetCY, outerColor); renderVB(window->fourByThreeSafeMargin, targetCX, targetCY, outerColor); renderVB(window->leftLine, targetCX, targetCY, outerColor); renderVB(window->topLine, targetCX, targetCY, outerColor); renderVB(window->rightLine, targetCX, targetCY, outerColor); } endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(previewLabel); gs_matrix_pop(); } /* ----------------------------- */ /* draw program */ obs_source_t *programLabel = window->multiviewLabels[1]; window->offset = labelOffset(programLabel, window->pvwprgCX); calcPreviewProgram(true); paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the program gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); obs_render_main_texture(); endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(programLabel); gs_matrix_pop(); } // Region for future usage with aditional info. if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) { // Just paint the background for now paintAreaWithColor(window->thickness, window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); paintAreaWithColor(window->thickness + 2.5 * ( window->thicknessx2 + window->ppiCX), window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); } endRegion(); }
/* * This function is called (see bottom of this file for more details) * whenever the OBS filter interface changes. So when the user is messing * with a slider this function is called to update the internal settings * in OBS, and hence the settings being passed to the CPU/GPU. */ static void color_correction_filter_update(void *data, obs_data_t *settings) { struct color_correction_filter_data *filter = data; /* Build our Gamma numbers. */ double gamma = obs_data_get_double(settings, SETTING_GAMMA); gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0)); vec3_set(&filter->gamma, (float)gamma, (float)gamma, (float)gamma); /* Build our contrast number. */ filter->contrast = (float)obs_data_get_double(settings, SETTING_CONTRAST) + 1.0f; float one_minus_con = (1.0f - filter->contrast) / 2.0f; /* Now let's build our Contrast matrix. */ filter->con_matrix = (struct matrix4) { filter->contrast, 0.0f, 0.0f, 0.0f, 0.0f, filter->contrast, 0.0f, 0.0f, 0.0f, 0.0f, filter->contrast, 0.0f, one_minus_con, one_minus_con, one_minus_con, 1.0f }; /* Build our brightness number. */ filter->brightness = (float)obs_data_get_double(settings, SETTING_BRIGHTNESS); /* * Now let's build our Brightness matrix. * Earlier (in the function color_correction_filter_create) we set * this matrix to the identity matrix, so now we only need * to set the 3 variables that have changed. */ filter->bright_matrix.t.x = filter->brightness; filter->bright_matrix.t.y = filter->brightness; filter->bright_matrix.t.z = filter->brightness; /* Build our Saturation number. */ filter->saturation = (float)obs_data_get_double(settings, SETTING_SATURATION) + 1.0f; /* Factor in the selected color weights. */ float one_minus_sat = (1.0f - filter->saturation) / 3.0f; float sat_val = one_minus_sat + filter->saturation; /* Now we build our Saturation matrix. */ filter->sat_matrix = (struct matrix4) { sat_val, one_minus_sat, one_minus_sat, 0.0f, one_minus_sat, sat_val, one_minus_sat, 0.0f, one_minus_sat, one_minus_sat, sat_val, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; /* Build our Hue number. */ filter->hue_shift = (float)obs_data_get_double(settings, SETTING_HUESHIFT); /* Build our Transparency number. */ filter->opacity = (float)obs_data_get_int(settings, SETTING_OPACITY) * 0.01f; /* Hue is the radian of 0 to 360 degrees. */ float half_angle = 0.5f * (float)(filter->hue_shift / (180.0f / M_PI)); /* Pseudo-Quaternion To Matrix. */ float rot_quad1 = root3 * (float)sin(half_angle); vec3_set(&filter->rot_quaternion, rot_quad1, rot_quad1, rot_quad1); filter->rot_quaternion_w = (float)cos(half_angle); vec3_mul(&filter->cross, &filter->rot_quaternion, &filter->rot_quaternion); vec3_mul(&filter->square, &filter->rot_quaternion, &filter->rot_quaternion); vec3_mulf(&filter->wimag, &filter->rot_quaternion, filter->rot_quaternion_w); vec3_mulf(&filter->square, &filter->square, 2.0f); vec3_sub(&filter->diag, &filter->half_unit, &filter->square); vec3_add(&filter->a_line, &filter->cross, &filter->wimag); vec3_sub(&filter->b_line, &filter->cross, &filter->wimag); /* Now we build our Hue and Opacity matrix. */ filter->hue_op_matrix = (struct matrix4) { filter->diag.x * 2.0f, filter->b_line.z * 2.0f, filter->a_line.y * 2.0f, 0.0f, filter->a_line.z * 2.0f, filter->diag.y * 2.0f, filter->b_line.x * 2.0f, 0.0f, filter->b_line.y * 2.0f, filter->a_line.x * 2.0f, filter->diag.z * 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, filter->opacity }; /* Now get the overlay color data. */ uint32_t color = (uint32_t)obs_data_get_int(settings, SETTING_COLOR); vec4_from_rgba(&filter->color, color); /* * Now let's build our Color 'overlay' matrix. * Earlier (in the function color_correction_filter_create) we set * this matrix to the identity matrix, so now we only need * to set the 6 variables that have changed. */ filter->color_matrix.x.x = filter->color.x; filter->color_matrix.y.y = filter->color.y; filter->color_matrix.z.z = filter->color.z; filter->color_matrix.t.x = filter->color.w * filter->color.x; filter->color_matrix.t.y = filter->color.w * filter->color.y; filter->color_matrix.t.z = filter->color.w * filter->color.z; /* First we apply the Contrast & Brightness matrix. */ matrix4_mul(&filter->final_matrix, &filter->bright_matrix, &filter->con_matrix); /* Now we apply the Saturation matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->sat_matrix); /* Next we apply the Hue+Opacity matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->hue_op_matrix); /* Lastly we apply the Color Wash matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->color_matrix); } /* * Since this is C we have to be careful when destroying/removing items from * OBS. Jim has added several useful functions to help keep memory leaks to * a minimum, and handle the destruction and construction of these filters. */ static void color_correction_filter_destroy(void *data) { struct color_correction_filter_data *filter = data; if (filter->effect) { obs_enter_graphics(); gs_effect_destroy(filter->effect); obs_leave_graphics(); } bfree(data); } /* * When you apply a filter OBS creates it, and adds it to the source. OBS also * starts rendering it immediately. This function doesn't just 'create' the * filter, it also calls the render function (farther below) that contains the * actual rendering code. */ static void *color_correction_filter_create(obs_data_t *settings, obs_source_t *context) { /* * Because of limitations of pre-c99 compilers, you can't create an * array that doesn't have a known size at compile time. The below * function calculates the size needed and allocates memory to * handle the source. */ struct color_correction_filter_data *filter = bzalloc(sizeof(struct color_correction_filter_data)); /* * By default the effect file is stored in the ./data directory that * your filter resides in. */ char *effect_path = obs_module_file("color_correction_filter.effect"); filter->context = context; /* Set/clear/assign for all necessary vectors. */ vec3_set(&filter->half_unit, 0.5f, 0.5f, 0.5f); matrix4_identity(&filter->bright_matrix); matrix4_identity(&filter->color_matrix); /* Here we enter the GPU drawing/shader portion of our code. */ obs_enter_graphics(); /* Load the shader on the GPU. */ filter->effect = gs_effect_create_from_file(effect_path, NULL); /* If the filter is active pass the parameters to the filter. */ if (filter->effect) { filter->gamma_param = gs_effect_get_param_by_name( filter->effect, SETTING_GAMMA); filter->final_matrix_param = gs_effect_get_param_by_name( filter->effect, "color_matrix"); } obs_leave_graphics(); bfree(effect_path); /* * If the filter has been removed/deactivated, destroy the filter * and exit out so we don't crash OBS by telling it to update * values that don't exist anymore. */ if (!filter->effect) { color_correction_filter_destroy(filter); return NULL; } /* * It's important to call the update function here. If we don't * we could end up with the user controlled sliders and values * updating, but the visuals not updating to match. */ color_correction_filter_update(filter, settings); return filter; } /* This is where the actual rendering of the filter takes place. */ static void color_correction_filter_render(void *data, gs_effect_t *effect) { struct color_correction_filter_data *filter = data; if (!obs_source_process_filter_begin(filter->context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) return; /* Now pass the interface variables to the .effect file. */ gs_effect_set_vec3(filter->gamma_param, &filter->gamma); gs_effect_set_matrix4(filter->final_matrix_param, &filter->final_matrix); obs_source_process_filter_end(filter->context, filter->effect, 0, 0); UNUSED_PARAMETER(effect); } /* * This function sets the interface. the types (add_*_Slider), the type of * data collected (int), the internal name, user-facing name, minimum, * maximum and step values. While a custom interface can be built, for a * simple filter like this it's better to use the supplied functions. */ static obs_properties_t *color_correction_filter_properties(void *data) { obs_properties_t *props = obs_properties_create(); obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -3.0f, 3.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, -2.0f, 2.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, TEXT_BRIGHTNESS, -1.0f, 1.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_SATURATION, TEXT_SATURATION, -1.0f, 5.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_HUESHIFT, TEXT_HUESHIFT, -180.0f, 180.0f, 0.01f); obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); obs_properties_add_color(props, SETTING_COLOR, TEXT_COLOR); UNUSED_PARAMETER(data); return props; } /* * As the functions' namesake, this provides the default settings for any * options you wish to provide a default for. Try to select defaults that * make sense to the end user, or that don't effect the data. * *NOTE* this function is completely optional, as is providing a default * for any particular setting. */ static void color_correction_filter_defaults(obs_data_t *settings) { obs_data_set_default_double(settings, SETTING_GAMMA, 0.0); obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0); obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0); obs_data_set_default_double(settings, SETTING_SATURATION, 0.0); obs_data_set_default_double(settings, SETTING_HUESHIFT, 0.0); obs_data_set_default_double(settings, SETTING_OPACITY, 100.0); obs_data_set_default_int(settings, SETTING_COLOR, 0xFFFFFF); } /* * So how does OBS keep track of all these plug-ins/filters? How does OBS know * which function to call when it needs to update a setting? Or a source? Or * what type of source this is? * * OBS does it through the obs_source_info_struct. Notice how variables are * assigned the name of a function? Notice how the function name has the * variable name in it? While not mandatory, it helps a ton for you (and those * reading your code) to follow this convention. */ struct obs_source_info color_filter = { .id = "color_filter", .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO, .get_name = color_correction_filter_name, .create = color_correction_filter_create, .destroy = color_correction_filter_destroy, .video_render = color_correction_filter_render, .update = color_correction_filter_update, .get_properties = color_correction_filter_properties, .get_defaults = color_correction_filter_defaults };
static void update_item_transform(struct obs_scene_item *item) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); struct vec2 base_origin; struct vec2 origin; struct vec2 scale = item->scale; struct calldata params; uint8_t stack[128]; if (os_atomic_load_long(&item->defer_update) > 0) return; width = cx; height = cy; vec2_zero(&base_origin); vec2_zero(&origin); /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { calculate_bounds_data(item, &origin, &scale, &cx, &cy); } else { cx = (uint32_t)((float)cx * scale.x); cy = (uint32_t)((float)cy * scale.y); } add_alignment(&origin, item->align, (int)cx, (int)cy); matrix4_identity(&item->draw_transform); matrix4_scale3f(&item->draw_transform, &item->draw_transform, scale.x, scale.y, 1.0f); matrix4_translate3f(&item->draw_transform, &item->draw_transform, -origin.x, -origin.y, 0.0f); matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->draw_transform, &item->draw_transform, item->pos.x, item->pos.y, 0.0f); item->output_scale = scale; /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { vec2_copy(&scale, &item->bounds); } else { scale.x = (float)width * item->scale.x; scale.y = (float)height * item->scale.y; } add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y); matrix4_identity(&item->box_transform); matrix4_scale3f(&item->box_transform, &item->box_transform, scale.x, scale.y, 1.0f); matrix4_translate3f(&item->box_transform, &item->box_transform, -base_origin.x, -base_origin.y, 0.0f); matrix4_rotate_aa4f(&item->box_transform, &item->box_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->box_transform, &item->box_transform, item->pos.x, item->pos.y, 0.0f); /* ----------------------- */ item->last_width = width; item->last_height = height; calldata_init_fixed(¶ms, stack, sizeof(stack)); calldata_set_ptr(¶ms, "scene", item->parent); calldata_set_ptr(¶ms, "item", item); signal_handler_signal(item->parent->source->context.signals, "item_transform", ¶ms); }
static void recalculate_transition_matrix(obs_source_t *tr, size_t idx) { obs_source_t *child; struct matrix4 mat; struct vec2 pos; struct vec2 scale; float tr_cx = (float)tr->transition_actual_cx; float tr_cy = (float)tr->transition_actual_cy; float source_cx; float source_cy; float tr_aspect = tr_cx / tr_cy; float source_aspect; enum obs_transition_scale_type scale_type = tr->transition_scale_type; lock_transition(tr); child = tr->transition_sources[idx]; if (!child) { unlock_transition(tr); return; } source_cx = (float)obs_source_get_width(child); source_cy = (float)obs_source_get_height(child); unlock_transition(tr); if (source_cx == 0.0f || source_cy == 0.0f) return; source_aspect = source_cx / source_cy; if (scale_type == OBS_TRANSITION_SCALE_MAX_ONLY) { if (source_cx > tr_cx || source_cy > tr_cy) { scale_type = OBS_TRANSITION_SCALE_ASPECT; } else { scale.x = 1.0f; scale.y = 1.0f; } } if (scale_type == OBS_TRANSITION_SCALE_ASPECT) { bool use_width = tr_aspect < source_aspect; scale.x = scale.y = use_width ? tr_cx / source_cx : tr_cy / source_cy; } else if (scale_type == OBS_TRANSITION_SCALE_STRETCH) { scale.x = tr_cx / source_cx; scale.y = tr_cy / source_cy; } source_cx *= scale.x; source_cy *= scale.y; vec2_zero(&pos); add_alignment(&pos, tr->transition_alignment, (int)(tr_cx - source_cx), (int)(tr_cy - source_cy)); matrix4_identity(&mat); matrix4_scale3f(&mat, &mat, scale.x, scale.y, 1.0f); matrix4_translate3f(&mat, &mat, pos.x, pos.y, 0.0f); matrix4_copy(&tr->transition_matrices[idx], &mat); }
Matrix4 testMatrix4Transposed2(const Matrix4& a) { return matrix4_for_expression( matrix_transposed( matrix4_identity(a) ) ); }