package electrosphere.renderer.ui.components; import java.util.stream.Collectors; import org.joml.Vector3f; import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.engine.Globals; import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.game.data.creature.type.CreatureData; import electrosphere.game.data.creature.type.visualattribute.AttributeVariant; import electrosphere.game.data.creature.type.visualattribute.VisualAttribute; import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.ActorStaticMorph; import electrosphere.renderer.actor.ActorUtils; import electrosphere.renderer.ui.elements.ActorPanel; import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.Div; import electrosphere.renderer.ui.elements.Label; import electrosphere.renderer.ui.elements.ScrollableContainer; import electrosphere.renderer.ui.elements.Slider; import electrosphere.renderer.ui.elements.StringCarousel; import electrosphere.renderer.ui.elementtypes.Element; import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.util.Utilities; /** * Panel to customize a character */ public class CharacterCustomizer { /** * Minimum width of the component */ public static final int MIN_WIDTH = 500; /** * Minimum height of the component */ public static final int MIN_HEIGHT = 500; /** * Creates a character customizer panel * @param race The race of the character * @return The panel component */ public static Element createCharacterCustomizerPanel(String race){ //figure out race data CreatureData selectedRaceType = Globals.gameConfigCurrent.getCreatureTypeLoader().getType(race); //spawn camera so renderer doesn't crash (once render pipeline is modularized this shouldn't be necessary) Globals.playerCamera = CameraEntityUtils.spawnBasicCameraEntity(new Vector3f(0,0,0), new Vector3f(0,0.3f,1).normalize()); Globals.viewMatrix = CameraEntityUtils.getCameraViewMatrix(Globals.playerCamera); //create actor panel Actor characterActor = ActorUtils.createActorFromModelPath(selectedRaceType.getModelPath()); ActorPanel actorPanel = ActorPanel.create(characterActor); actorPanel.setAnimation(selectedRaceType.getIdleData().getAnimation().getNameThirdPerson()); actorPanel.setPosition(new Vector3f(0,-0.5f,-0.6f)); actorPanel.setScale(new Vector3f(1.0f)); //have to build static morph while looping through attributes ActorStaticMorph staticMorph = new ActorStaticMorph(); //create creature template CreatureTemplate template = CreatureTemplate.create(race); //create scrollable ScrollableContainer scrollable = ScrollableContainer.createScrollable(); scrollable.setMinWidth(MIN_WIDTH); scrollable.setMinHeight(MIN_HEIGHT); //create edit controls here for(VisualAttribute attribute : selectedRaceType.getVisualAttributes()){ if(attribute.getType().equals(VisualAttribute.TYPE_BONE)){ //add label for slider Label sliderName = new Label(0.6f); sliderName.setText(attribute.getAttributeId()); sliderName.setMinWidth(200); //add a slider Slider boneSlider = Slider.createSlider((ValueChangeEvent event) -> { if(characterActor.getStaticMorph() != null){ staticMorph.updateValue(attribute.getSubtype(), attribute.getPrimaryBone(), event.getAsFloat()); if(attribute.getMirrorBone() != null){ staticMorph.updateValue(attribute.getSubtype(), attribute.getMirrorBone(), event.getAsFloat()); } template.getAttributeValue(attribute.getAttributeId()).setValue(event.getAsFloat()); } }); float min = attribute.getMinValue(); float max = attribute.getMaxValue(); float defaultValue = min + (max - min)/2.0f; boneSlider.setMinimum(min); boneSlider.setMaximum(max); boneSlider.setValue(defaultValue); boneSlider.setMinWidth(200); //actually add attributes to static morph if(attribute.getPrimaryBone() != null && staticMorph.getBoneTransforms(attribute.getPrimaryBone()) == null){ staticMorph.initBoneTransforms(attribute.getPrimaryBone()); } if(attribute.getMirrorBone() != null && staticMorph.getBoneTransforms(attribute.getMirrorBone()) == null){ staticMorph.initBoneTransforms(attribute.getMirrorBone()); } //add attribute to creature template template.putAttributeValue(attribute.getAttributeId(), defaultValue); scrollable.addChild(Div.createRow(sliderName,boneSlider)); } else if(attribute.getType().equals(VisualAttribute.TYPE_REMESH)){ //add label for carousel Label scrollableName = new Label(0.6f); scrollableName.setText(attribute.getAttributeId()); scrollableName.setMinWidth(200); //add a carousel StringCarousel variantCarousel = StringCarousel.create( // attribute.getVariants().stream().filter(variant -> ), attribute.getVariants().stream().map(variant -> variant.getId()).collect(Collectors.toList()), (ValueChangeEvent event) -> { //TODO: implement updating visuals template.getAttributeValue(attribute.getAttributeId()).setVariantId(event.getAsString()); AttributeVariant variant = null; for(AttributeVariant variantCurrent : attribute.getVariants()){ if(variantCurrent.getId().equals(event.getAsString())){ variant = variantCurrent; break; } } if(variant != null){ Globals.assetManager.addModelPathToQueue(variant.getModel()); for(String mesh : variant.getMeshes()){ characterActor.getMeshMask().queueMesh(variant.getModel(), mesh); } } } ); variantCarousel.setMinWidth(200); //set the current attrib for the template template.putAttributeValue(attribute.getAttributeId(), attribute.getVariants().get(0).getId()); scrollable.addChild(Div.createRow(scrollableName,variantCarousel)); } } //finally set static morph characterActor.setActorStaticMorph(staticMorph); //create layout Div rVal = Div.createCol( Div.createRow( scrollable, actorPanel ), Button.createButton("Create", () -> { Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestCreateCharacterMessage(Utilities.stringify(template))); }) ); return rVal; } }