#if SPECIAL #include "main.h" #endif static property_channel Property_InitFloat(real32 Val, real32 ScrubVal, real32 MinVal = PROPERTY_REAL_MIN, real32 MaxVal = PROPERTY_REAL_MAX, bool32 AlwaysInteger = 0); static block_layer * Layer_Init(project_data *File, memory *Memory) { if (File->Layer_Count + 1 > MAX_LAYERS) { Assert(0); } block_layer *Layer = (block_layer *)Memory_Block_AllocateAddress(Memory, F_Layers); History_Action_Block_Swap(Memory, F_Layers, Layer); *Layer = {}; Layer->Occupied = 1; Layer->Block_String_Index = Memory_Block_AllocateNew(Memory, F_Strings); block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index, 0); sprintf(String->Char, "Layer %i", File->Layer_Count + 1); // CSbros... History_Action_Swap(Memory, F_Strings, sizeof(String->Occupied), &String->Occupied); String->Occupied = 1; Layer->x = Property_InitFloat(0.0f, 1.0f); Layer->y = Property_InitFloat(0.0f, 1.0f); Layer->ax = Property_InitFloat(0.5f, 0.005f); Layer->ay = Property_InitFloat(0.5f, 0.005f); Layer->scale = Property_InitFloat(1.0f, 0.005f); Layer->rotation = Property_InitFloat(0.0f, 1.0f); Layer->opacity = Property_InitFloat(1.0f, 0.005f, 0.0f, 1.0f); Layer->time = Property_InitFloat(0.0f, 1.0f, 0, 100000, 1); Layer->IsVisible = 1; History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count); File->Layer_Count++; return Layer; } // lots of cleanup... static void Layer_Delete(project_data *File, project_state *State, memory *Memory, uint32 Index) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index); History_Action_Block_Swap(Memory, F_Layers, Layer); Layer->Occupied = 0; block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index, 0); History_Action_Block_Swap(Memory, F_Strings, String); String->Occupied = 0; for (int i = 0; i < Layer->Block_Effect_Count; i++) { block_effect *Effect = (block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, Layer->Block_Effect_Index[i]); header_effect *EffectHeader = Effect_EntryFromID(State, Effect->ID); for (int h = 0; h < EffectHeader->Property_Count; h++) { header_property ChannelHeader = State->Property[EffectHeader->PropertyStartIndex + h]; property_channel *Property = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[h]); if (Property->Block_Bezier_Count) { block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Property->Block_Bezier_Index[i], 0); History_Action_Block_Swap(Memory, F_Bezier, Bezier); Bezier->Occupied = 0; } History_Action_Block_Swap(Memory, F_Properties, Property); Property->Occupied = 0; } History_Action_Block_Swap(Memory, F_Effects, Effect); Effect->Occupied = 0; } History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count); File->Layer_Count--; } static layer_transforms Layer_GetTransforms(block_layer *Layer) { return { Layer->x.CurrentValue, Layer->y.CurrentValue, Layer->ax.CurrentValue, Layer->ay.CurrentValue, Layer->rotation.CurrentValue, Layer->scale.CurrentValue }; } static int Layer_GetTopOffset(project_data *File, memory *Memory) { if (File->Layer_Count == 0) return 11; int TopOffset = 9999; int h = 0, c = 0, i = 0; while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->Block_Composition_Index == File->PrincipalCompIndex) { TopOffset = (Layer->Vertical_Offset < TopOffset) ? Layer->Vertical_Offset : TopOffset; } } return TopOffset; } static void Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memory, void *EffectBitmapAddress, int Width, int Height, int BytesPerPixel) { uint64 Size = Width*Height*BytesPerPixel; // We need two of these: one with multisampling enabled and a // non-multisampled one that we can blit to. gl_effect_layer TestL = {}; gl_effect_layer TestM = {}; GL_UpdateTexture(&TestL, EffectBitmapAddress, Width, Height, BytesPerPixel, 0); GL_UpdateTexture(&TestM, EffectBitmapAddress, Width, Height, BytesPerPixel, 1); if (Layer->IsShapeLayer) { shape_layer *Shape = &Layer->Shape; shape_options ShapeOpt = Layer->ShapeOpt; void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128); Arbitrary_Zero((uint8 *)Data, sizeof(nvg_point) * 128); layer_transforms T = Layer_GetTransforms(Layer); v2 Min = {}, Max = {}; uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, Shape->Width, Shape->Height, Width, Height, 1, &Min, &Max); void *Data_Stroke = Memory_PushScratch(Memory, sizeof(real32) * 4 * 256); uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke); void *Data_Fill = Memory_PushScratch(Memory, sizeof(real32) * 4 * NumberOfVerts); NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill); Assert(0); // GL_RasterizeShape(&TestL, &TestM, Data_Stroke, Data_Fill, StrokeCount, NumberOfVerts, T, // Width, Height, BytesPerPixel, EffectBitmapAddress, Shape->Width, Shape->Height, // Shape->Opt.StrokeCol, Shape->Opt.FillCol, Shape->Opt.Visibility); Memory_PopScratch(Memory, sizeof(real32) * 4 * NumberOfVerts); Memory_PopScratch(Memory, sizeof(real32) * 4 * 256); Memory_PopScratch(Memory, sizeof(nvg_point) * 128); // Bitmap_StencilAlpha(SourceBitmapAddress, EffectBitmapAddress, Source->BytesPerPixel, Size); } for (int i = 0; i < Layer->Block_Effect_Count; i++) { block_effect Effect = *(block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, Layer->Block_Effect_Index[i]); header_effect *EffectEntry = Effect_EntryFromID(State, Effect.ID); if (Effect.IsToggled) { uint64 Size = (sizeof(real32) * MAX_PROPERTIES_PER_EFFECT) + (sizeof(real32) * 10); real32 *Data; if (EffectEntry->DisplayType == effect_display_type_curves) { Data = (real32 *)Memory_PushScratch(Memory, Size); uint16 SortedPointIndex[MAX_PROPERTIES_PER_EFFECT]; v2 *SortedPointValues = (v2 *)(Data + 5); for (int c = 0; c < 5; c++) { *(Data + c) = Effect.ExtraData[c]; uint32 Shift = MAX_PROPERTIES_PER_EFFECT / 5 * c; uint16 *SortedPointIndexPlayhead = SortedPointIndex + Shift; v2 *SortedPointValuesPlayhead = SortedPointValues + Shift; Effect_Curves_Sort(Memory, &Effect, SortedPointIndexPlayhead, c); for (int a = 0; a < Effect.ExtraData[c]; a++) { *SortedPointValuesPlayhead = Effect_V2(Memory, &Effect, SortedPointIndexPlayhead[a]); SortedPointValuesPlayhead++; } } } else { Data = (real32 *)Memory_PushScratch(Memory, Size); for (int c = 0; c < EffectEntry->Property_Count; c++) { property_channel *Property = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect.Block_Property_Index[c]); Data[c] = Property->CurrentValue; } } EffectEntry->func(Data, Width, Height, BytesPerPixel, EffectBitmapAddress, EffectEntry->GLShaderIndex); Memory_PopScratch(Memory, Size); } } GL_DeleteHWBuffer(&TestL); GL_DeleteHWBuffer(&TestM); } static void Layer_ToggleChannel(project_data *File, memory *Memory, int32 a) { int h = 0, c = 0, i = 0; while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected & 0x01) Layer->Property[a].IsToggled ^= 1; } } static void Layer_Select(memory *Memory, project_state *State, int32 i) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); // Assert(!(Layer->IsSelected & 0x02)); Layer->IsSelected |= 0x01; State->MostRecentlySelectedLayer = i; State->Interact_Transform = {}; State->RecentSelectionType = selection_type_layer; } static void Layer_Select_RecurseUp(memory *Memory, project_state *State, int32 i, int16 RecursionIdx[MAX_PRECOMP_RECURSIONS], uint32 Recursions) { for (int a = 1; a <= Recursions; a++) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, RecursionIdx[a]); Layer->IsSelected = 0x02; } } static void Interact_BoxSelect_Start(project_data *File, project_state *State, memory *Memory) { int h = 0, c = 0, i = 0; while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected & 0x01) { Layer->IsSelected |= 0x04; } } } static void Interact_BoxSelect_End(project_data *File, project_state *State, memory *Memory) { int h = 0, c = 0, i = 0; while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected & 0x04) { Layer->IsSelected ^= 0x04; } } } static void Layer_DeselectAll(project_data *File, project_state *State, memory *Memory, bool32 AllowComp) { int h = 0, c = 0, i = 0; bool32 ShiftLayers = false; while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected & 0x04) { ShiftLayers = 1; } else { if (!(AllowComp && Layer->IsSelected & 0x02)) { Layer->IsSelected = 0x00; } } } State->Interact_Transform = {}; if (!ShiftLayers) { State->MostRecentlySelectedLayer = -1; } } // TODO(fox): This function will be replaced once properties are reworked. // h: index of the total amount of properties and effects // c: index of the amount of properties in a given effect // p: prior property's keyframe count, so we can increment the sorted keyframe array properly static bool32 Layer_LoopChannels(project_state *State, memory *Memory, sorted_property_array **SortedProperty, uint16 **SortedKeyframe, block_layer *Layer, property_channel **Property, block_effect **EffectOut, int *h, int *c, int *p, int LoopNumber) { uint32 Amount = LoopNumber + Layer->Block_Effect_Count; // Assert(Layer->Block_Effect_Count < 2); while (*h < Amount) { if (*h < LoopNumber && *c == 0) { *Property = &Layer->Property[*h]; if (*h != 0) { *SortedProperty += 1; *SortedKeyframe += *p; } *h += 1; *p = (**Property).Keyframe_Count; return 1; } else { uint16 EffectIdx = Layer->Block_Effect_Index[*h - AmountOf(Layer->Property)]; block_effect *Effect = (block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, EffectIdx); if (EffectOut) *EffectOut = Effect; header_effect *EffectHeader = Effect_EntryFromID(State, Effect->ID); while (*c < EffectHeader->Property_Count) { // header_property ChannelHeader = State->Property[EffectHeader->PropertyStartIndex + c]; *Property = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[*c]); *SortedProperty += 1; *SortedKeyframe += *p; *p = (**Property).Keyframe_Count; *c += 1; return 1; } *h += 1; *c = 0; } } Assert(*h != (Amount - 1)); return 0; } static void Layer_ToggleAllChannels(project_state *State, memory *Memory, block_layer *Layer, sorted_comp_array *SortedCompStart, sorted_layer_array *SortedLayerStart, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray) { bool32 ToggleMode = 1; { sorted_property_array *InfoLocation = SortedPropertyStart + SortedLayerStart->SortedPropertyStart; uint16 *ArrayLocation = SortedKeyframeArray + SortedLayerStart->SortedKeyframeStart; int h = 0, c = 0, p = 0; property_channel *Property = NULL; block_effect *Effect = NULL; while (Layer_LoopChannels(State, Memory, &InfoLocation, &ArrayLocation, Layer, &Property, &Effect, &h, &c, &p)) { if (Property->IsToggled) { ToggleMode = 0; break; } } } sorted_property_array *InfoLocation = SortedPropertyStart + SortedLayerStart->SortedPropertyStart; uint16 *ArrayLocation = SortedKeyframeArray + SortedLayerStart->SortedKeyframeStart; int h = 0, c = 0, p = 0; property_channel *Property = NULL; block_effect *Effect = NULL; while (Layer_LoopChannels(State, Memory, &InfoLocation, &ArrayLocation, Layer, &Property, &Effect, &h, &c, &p)) { if (Property->Keyframe_Count) { Property->IsToggled = ToggleMode; } } } static void Layer_Select_Traverse(uint16 PrincipalCompIndex, memory *Memory, project_state *State, int32 IndexToFind, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, int16 RecursionIdx[MAX_PRECOMP_RECURSIONS], int32 *Recursions) { uint16 CompIndex = 0; if (RecursionIdx[*Recursions] == -1) { CompIndex = PrincipalCompIndex; } else { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, RecursionIdx[*Recursions]); CompIndex = Layer->Block_Source_Index; } sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); sorted_comp_array SortedCompStart = SortedCompArray[CompIndex]; uint32 RecursionsCurrent = *Recursions; for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsPrecomp && (Layer->IsSelected & 0x02)) { *Recursions = RecursionsCurrent + 1; RecursionIdx[*Recursions] = Index_Physical; Layer_Select_Traverse(PrincipalCompIndex, Memory, State, IndexToFind, SortedCompArray, SortedLayerArray, RecursionIdx, Recursions); } else if (Index_Physical == IndexToFind) { return; } } } static void Layer_GetDimensions(memory *Memory, block_layer *Layer, int *Width, int *Height) { if (Layer->IsPrecomp) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); *Width = Comp->Width; *Height = Comp->Height; } else if (Layer->IsShapeLayer) { *Width = Layer->Shape.Width; *Height = Layer->Shape.Height; } else { block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); *Width = Source->Width; *Height = Source->Height; } } static v2 Layer_TraverseForPoint(project_data *File, project_state *State, memory *Memory, v2 PrincipalCompUV, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 LayerIndex) { int16 RecursionIdx[MAX_PRECOMP_RECURSIONS] = {}; RecursionIdx[0] = -1; int32 Recursions = 0; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex); sorted_comp_array SortedCompStart = SortedCompArray[File->PrincipalCompIndex]; block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); Layer_Select_Traverse(File->PrincipalCompIndex, Memory, State, LayerIndex, SortedCompArray, SortedLayerArray, RecursionIdx, &Recursions); v2 PointUV = {0, 0}; int OuterWidth = Comp->Width, OuterHeight = Comp->Height; int InnerWidth = 0, InnerHeight = 0; if (Recursions == 0) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, LayerIndex); layer_transforms T = Layer_GetTransforms(Layer); Layer_GetDimensions(Memory, Layer, &InnerWidth, &InnerHeight); PointUV = T_CompUVToLayerUV(T, OuterWidth, OuterHeight, InnerWidth, InnerHeight, PrincipalCompUV); } else { for (int i = 1; i <= Recursions; i++) { block_layer *InnerLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, RecursionIdx[i]); layer_transforms T = Layer_GetTransforms(InnerLayer); Layer_GetDimensions(Memory, InnerLayer, &InnerWidth, &InnerHeight); PointUV = T_CompUVToLayerUV(T, OuterWidth, OuterHeight, InnerWidth, InnerHeight, PrincipalCompUV); OuterWidth = InnerWidth; OuterHeight = InnerHeight; } } return PointUV * V2(InnerWidth, InnerHeight); } // TODO(fox): Precomps? static bool32 Layer_TestForPoint(memory *Memory, project_state *State, ui *UI, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 CompIndex, v2 CompUV) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array SortedCompStart = SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int Width, Height; Layer_GetDimensions(Memory, Layer, &Width, &Height); layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, CompUV); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && ((Layer->IsSelected & 0x01) || Layer->IsPrecomp) && !Layer->IsLocked) { return 1; } } return 0; } // Same loop as Render_UI and ImGui_Viewport_SelectedLayerUI except we're // NOTE(fox): We loop twice to allow for convenient down-travelling when only // one layer is selected, toggleable by the BelowOnly bool (i.e. shift held). #if 0 static int32 Layer_TestSelection(memory *Memory, project_state *State, ui *UI, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 CompIndex, bool32 BelowOnly) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array SortedCompStart = SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); int SelectionCount = 0; int SelectedLayerIndex = 0; for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int Width, Height; Layer_GetDimensions(Memory, Layer, &Width, &Height); layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, State->LastClickedPoint); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && Layer->IsSelected) { SelectionCount++; SelectedLayerIndex = i; } } int32 LayerIndex = -1; for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int Width, Height; Layer_GetDimensions(Memory, Layer, &Width, &Height); layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, State->LastClickedPoint); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && (!Layer->IsSelected || (Layer->IsPrecomp && Layer->IsSelected)) && !Layer->IsLocked) { if (Layer->IsPrecomp) { layer_transforms T = Layer_GetTransforms(Layer); if (ExtraT.scale != 0) { T = Transform_Add(T, ExtraT, Comp->Width, Comp->Height); } T = Transform_TestInteracts(State, Layer, SortEntry, T); LayerIndex = Layer_TestSelection(Memory, State, UI, SortedCompArray, SortedLayerArray, Layer->Block_Source_Index, BelowOnly); if (LayerIndex != -1) break; } else { if (!BelowOnly && SelectionCount == 1) { if (i < SelectedLayerIndex) { LayerIndex = Index_Physical; break; } } else { LayerIndex = Index_Physical; break; } } } // if (Layer->IsPrecomp) { // Layer_RecursiveDeselect(Memory, SortedCompArray, SortedLayerArray, TargetIndex, Layer->Block_Source_Index); // } // if (Layer->Block_Composition_Index != TargetIndex) { // Layer->IsSelected = false; // } } return LayerIndex; } #endif // TODO(fox): learn this; stop stealing from stackoverflow bool32 Line_TestIntersect(real32 p0_x, real32 p0_y, real32 p1_x, real32 p1_y, real32 p2_x, real32 p2_y, real32 p3_x, real32 p3_y) { real32 s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; real32 b = (-s2_x * s1_y + s1_x * s2_y); if (b == 0) return 0; real32 s, t; s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / b; t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / b; if (s >= 0 && s <= 1) { int a = 0; } if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { return 1; } return 0; // No collision } static bool32 Transform_TestBox(block_layer *Layer, uint32 Width, uint32 Height, v2 Min, v2 Max) { real32 Rad = (Layer->rotation.CurrentValue * (PI / 180)); real32 s = Layer->scale.CurrentValue; v2 XAxis = (Width * s)*V2(cos(Rad), sin(Rad)); v2 YAxis = (Height * -s)*V2(sin(Rad), -cos(Rad)); real32 AnchorX = Layer->ax.CurrentValue; real32 AnchorY = Layer->ay.CurrentValue; v2 Pos = {Layer->x.CurrentValue, Layer->y.CurrentValue}; v2 Origin = Pos - (XAxis * AnchorX) - (YAxis * AnchorY); if (Origin.x > Min.x && Origin.x < Max.x && Origin.y > Min.y && Origin.y < Max.y) return true; real32 XLengthSq = 1.0f / LengthSq(XAxis); real32 YLengthSq = 1.0f / LengthSq(YAxis); v2 Box[4] = { Min, V2(Min.x, Max.y), Max, V2(Max.x, Min.y) }; v2 Point[4] = {Origin, Origin + YAxis, Origin + XAxis + YAxis, Origin + XAxis }; if (Length((Max - Min)) < 2.0f) return false; v2 *P1_B = &Box[3]; v2 *P2_B = &Box[0]; for (int x = 0; x < 4; x++) { v2 *P1 = &Point[3]; v2 *P2 = &Point[0]; for (int i = 0; i < 4; i++) { if (Line_TestIntersect(P1->x, P1->y, P2->x, P2->y, P1_B->x, P1_B->y, P2_B->x, P2_B->y)) return true; P1 = P2++; } P1_B = P2_B++; } return false; } // TODO(fox): It would probably be faster to transform the box into layer space // and test that way. Don't know how to do the UV test though. static bool32 Shape_TestBoxSelect(layer_transforms T, uint32 CompWidth, uint32 CompHeight, uint32 ShapeWidth, uint32 ShapeHeight, v2 Box[4], nvg_point *Point, uint16 PointCount) { // v2 BoxLocal[4] = {}; // for (int x = 0; x < 4; x++) { // BoxLocal[x] = T_CompPosToLayerPos(T, CompWidth, CompHeight, ShapeHeight, ShapeWidth, Box[x].x, Box[x].y); // } Assert(Box[0].x < Box[2].x && Box[0].y < Box[2].y); #if 1 for (int i = 0; i < PointCount; i++) { nvg_point *P0 = &Point[i]; v2 Pos0 = TransformPoint(T, ShapeWidth, ShapeHeight, V2(P0->x, P0->y)); if (Pos0.x > Box[0].x && Pos0.x < Box[2].x && Pos0.y > Box[0].y && Pos0.y < Box[2].y) return true; P0->x = Pos0.x; P0->y = Pos0.y; } #endif #if 1 nvg_point *P0 = &Point[PointCount-1]; nvg_point *P1 = &Point[0]; for (int i = 0; i < PointCount; i++) { if (Line_TestIntersect(P0->x, P0->y, P1->x, P1->y, Box[0].x, Box[0].y, Box[1].x, Box[1].y)) return true; if (Line_TestIntersect(P0->x, P0->y, P1->x, P1->y, Box[1].x, Box[1].y, Box[2].x, Box[2].y)) return true; if (Line_TestIntersect(P0->x, P0->y, P1->x, P1->y, Box[2].x, Box[2].y, Box[3].x, Box[3].y)) return true; if (Line_TestIntersect(P0->x, P0->y, P1->x, P1->y, Box[3].x, Box[3].y, Box[0].x, Box[0].y)) return true; P0 = P1++; } #endif return false; } static void Layer_TestBoxSelect(memory *Memory, project_state *State, ui *UI, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 PrincipalIndex, layer_transforms ExtraT, v2 MinPos, v2 MaxPos) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); sorted_comp_array SortedCompStart = SortedCompArray[PrincipalIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, PrincipalIndex); for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsLocked) continue; if (Layer->IsShapeLayer) { shape_layer *Shape = &Layer->Shape; int Width = Shape->Width, Height = Shape->Height; layer_transforms T = Layer_GetTransforms(Layer); nvg_point *NVGPointData = (nvg_point *)Memory_PushScratch(Memory, sizeof(nvg_point) * 1024); v2 ShapeMin = {}, ShapeMax = {}; uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, Layer->ShapeOpt, NVGPointData, State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &ShapeMin, &ShapeMax); v2 Box[4] = { MinPos, V2(MinPos.x, MaxPos.y), MaxPos, V2(MaxPos.x, MinPos.y) }; if (Shape_TestBoxSelect(T, Comp->Width, Comp->Height, Shape->Width, Shape->Height, Box, NVGPointData, NumberOfVerts)) { Layer_Select(Memory, State, Index_Physical); } Memory_PopScratch(Memory, sizeof(nvg_point) * 1024); } else if (Layer->IsPrecomp && (Layer->IsSelected & 0x02)) { int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); // only like 20% sure how this works... layer_transforms NewExtraT = Layer_GetTransforms(Layer); v2 NewMinPos = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, MinPos.x, MinPos.y); v2 NewMaxPos = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, MaxPos.x, MaxPos.y); NewExtraT.rotation = ExtraT.rotation - NewExtraT.rotation; NewExtraT.scale = ExtraT.scale / NewExtraT.scale; Layer_TestBoxSelect(Memory, State, UI, SortedCompArray, SortedLayerArray, Layer->Block_Source_Index, NewExtraT, NewMinPos, NewMaxPos); } else { int Width, Height; Layer_GetDimensions(Memory, Layer, &Width, &Height); if (Transform_TestBox(Layer, Width, Height, MinPos, MaxPos)) Layer_Select(Memory, State, Index_Physical); } } } static void Layer_RecursiveDeselect(memory *Memory, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 TargetIndex, uint16 PrincipalIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); sorted_comp_array SortedCompStart = SortedCompArray[PrincipalIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, PrincipalIndex); for (int i = 0; i < SortedCompStart.LayerCount; i++) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsPrecomp) { Layer_RecursiveDeselect(Memory, SortedCompArray, SortedLayerArray, TargetIndex, Layer->Block_Source_Index); } if (Layer->Block_Composition_Index != TargetIndex) { Layer->IsSelected = 0x00; } } }